Commit afae7aa1 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Significantly improve speed of vertex/edge iterators and descriptors in python interface

parent 973d73cf
......@@ -151,7 +151,8 @@ public:
// internal access
multigraph_t& GetGraph() {return *_mg;}
multigraph_t& GetGraph() {return *_mg;}
std::shared_ptr<multigraph_t> GetGraphPtr() {return _mg;}
vertex_index_map_t GetVertexIndex() {return _vertex_index;}
edge_index_map_t GetEdgeIndex() {return _edge_index;}
size_t GetMaxEdgeIndex(){return _mg->get_last_index();}
......@@ -160,6 +161,7 @@ public:
// Gets the encapsulated graph view. See graph_filtering.cc for details
boost::any GetGraphView() const;
vector<boost::any>& GetGraphViews() {return _graph_views;}
private:
......@@ -167,11 +169,6 @@ private:
template <class Action, class GraphViews, class Wrap, class... TRS>
friend struct detail::graph_action;
// python interface
friend class PythonVertex;
template <class Graph>
friend class PythonEdge;
// this is the main graph
shared_ptr<multigraph_t> _mg;
......@@ -206,6 +203,7 @@ private:
bool _edge_filter_active;
};
} //namespace graph_tool
#endif
......@@ -67,34 +67,10 @@ const char * graph_tool::ActionNotFound::what () const throw ()
return error.c_str();
}
// this function retrieves a graph view stored in graph_views, or stores one if
// non-existent
template <class Graph>
typename std::remove_const<Graph>::type&
retrieve_graph(vector<boost::any>& graph_views, Graph& init)
{
typedef typename std::remove_const<Graph>::type g_t;
size_t index = boost::mpl::find<all_graph_views,g_t>::type::pos::value;
if (index >= graph_views.size())
graph_views.resize(index+1);
boost::any gview = graph_views[index];
std::shared_ptr<g_t>* gptr = any_cast<std::shared_ptr<g_t> >(&gview);
if (gptr == 0)
{
std::shared_ptr<g_t> new_g(new g_t(init));
gptr = &new_g;
gview = new_g;
graph_views[index] = gview;
}
return **gptr;
}
// this will check whether a graph is reversed and return the proper view
// encapsulated
template <class Graph>
boost::any check_reverse(const Graph &g, bool reverse,
vector<boost::any>& graph_views)
boost::any check_reverse(const Graph& g, bool reverse, GraphInterface& gi)
{
if (reverse)
{
......@@ -104,26 +80,26 @@ boost::any check_reverse(const Graph &g, bool reverse,
reverse_graph_t;
reverse_graph_t rg(g);
return &retrieve_graph(graph_views, rg);
return retrieve_graph_view(gi, rg).get();
}
return boost::any(&const_cast<Graph&>(g));
return boost::any(const_cast<Graph*>(&g));
};
// this will check whether a graph is directed and return the proper view
// encapsulated
template <class Graph>
boost::any check_directed(const Graph &g, bool reverse, bool directed,
vector<boost::any>& graph_views)
GraphInterface& gi)
{
if (directed)
{
return check_reverse(g, reverse, graph_views);
return check_reverse(g, reverse, gi);
}
typedef UndirectedAdaptor<Graph> ug_t;
ug_t ug(g);
return &retrieve_graph(graph_views, ug);
return retrieve_graph_view(gi, ug).get();
};
// this will check whether a graph is filtered and return the proper view
......@@ -133,7 +109,7 @@ boost::any
check_filtered(const Graph &g, const EdgeFilter& edge_filter,
const bool& e_invert, bool e_active, size_t max_eindex,
const VertexFilter& vertex_filter, const bool& v_invert,
bool v_active, vector<boost::any>& graph_views, bool reverse,
bool v_active, GraphInterface& gi, bool reverse,
bool directed)
{
#ifndef NO_GRAPH_FILTERING
......@@ -152,20 +128,20 @@ check_filtered(const Graph &g, const EdgeFilter& edge_filter,
typedef filtered_graph<Graph, MaskFilter<EdgeFilter>,
MaskFilter<VertexFilter> > fg_t;
fg_t init(g, e_filter, v_filter);
fg_t& fg = retrieve_graph(graph_views, init);
fg_t& fg = *retrieve_graph_view(gi, init).get();
fg.m_edge_pred = e_filter;
fg.m_vertex_pred = v_filter;
return check_directed(fg, reverse, directed, graph_views);
return check_directed(fg, reverse, directed, gi);
}
else
{
if (v_active)
throw GraphException("Vertex filter is active but edge filter is not. This is a bug.");
return check_directed(g, reverse, directed, graph_views);
return check_directed(g, reverse, directed, gi);
}
#else
return check_directed(g, reverse, directed, graph_views);
return check_directed(g, reverse, directed, gi);
#endif
}
......@@ -177,7 +153,7 @@ boost::any GraphInterface::GetGraphView() const
_edge_filter_active, _mg->get_last_index(),
_vertex_filter_map, _vertex_filter_invert,
_vertex_filter_active,
const_cast<vector<boost::any>&>(_graph_views), _reversed,
const_cast<GraphInterface&>(*this), _reversed,
_directed);
return graph;
}
......
......@@ -515,6 +515,43 @@ struct run_action
// returns true if graph filtering was enabled at compile time
bool graph_filtering_enabled();
template <class Graph, class GraphInit>
std::shared_ptr<Graph> get_graph_ptr(GraphInterface& gi, GraphInit&, std::true_type)
{
return gi.GetGraphPtr();
}
template <class Graph, class GraphInit>
std::shared_ptr<Graph> get_graph_ptr(GraphInterface&, GraphInit& g, std::false_type)
{
return std::shared_ptr<Graph>(new Graph(g));
}
// this function retrieves a graph view stored in graph_views, or stores one if
// non-existent
template <class Graph>
typename std::shared_ptr<Graph>
retrieve_graph_view(GraphInterface& gi, Graph& init)
{
typedef typename std::remove_const<Graph>::type g_t;
size_t index = boost::mpl::find<detail::all_graph_views,g_t>::type::pos::value;
auto& graph_views = gi.GetGraphViews();
if (index >= graph_views.size())
graph_views.resize(index + 1);
boost::any& gview = graph_views[index];
std::shared_ptr<g_t>* gptr = boost::any_cast<std::shared_ptr<g_t>>(&gview);
if (gptr == 0)
{
std::shared_ptr<g_t> new_g =
get_graph_ptr<g_t>(gi, init,
std::is_same<g_t, GraphInterface::multigraph_t>());
gptr = &new_g;
gview = new_g;
}
return *gptr;
}
} //graph_tool namespace
#endif // FILTERING_HH
This diff is collapsed.
......@@ -58,24 +58,23 @@ namespace graph_tool
// generic iterator adaptor which can be used to iterate vertices, edges,
// out_edges and in_edges through python
template <class Descriptor, class Iterator>
template <class Graph, class Descriptor, class Iterator>
class PythonIterator
{
public:
PythonIterator(const boost::python::object& g,
PythonIterator(std::shared_ptr<Graph>& gp,
std::pair<Iterator,Iterator> e)
: _wg(g), _g(g()), _e(e) {}
: _g(gp), _e(e) {}
Descriptor Next()
{
if (_e.first == _e.second)
boost::python::objects::stop_iteration_error();
Descriptor e(_wg, *_e.first);
Descriptor e(_g, *_e.first);
++_e.first;
return e;
}
private:
boost::python::object _wg;
boost::python::object _g;
std::shared_ptr<Graph> _g;
std::pair<Iterator,Iterator> _e;
};
......@@ -84,27 +83,24 @@ private:
template <class Graph>
class PythonEdge;
class VertexBase {}; // useful to unite all vertex
// below are classes related to the PythonVertex type
class PythonVertex
template <class Graph>
class PythonVertex : public VertexBase
{
public:
PythonVertex(const boost::python::object& g, GraphInterface::vertex_t v):
_g(g), _v(v), _valid(true)
{}
PythonVertex(std::shared_ptr<Graph> g, GraphInterface::vertex_t v):
_g(g), _v(v) {}
bool IsValid() const
{
if (_g().ptr() == Py_None)
std::shared_ptr<Graph> gp(_g);
Graph* g = gp.get();
if (g == nullptr)
return false;
GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
return _valid &&
(_v != boost::graph_traits<GraphInterface::multigraph_t>::null_vertex()) &&
(_v < num_vertices(*gi._mg));
}
void SetValid(bool valid)
{
_valid = valid;
return ((_v != boost::graph_traits<Graph>::null_vertex()) &&
(_v < num_vertices(*g)));
}
void CheckValid() const
......@@ -114,11 +110,6 @@ public:
boost::lexical_cast<string>(_v));
}
boost::python::object GetGraph() const
{
return _g();
}
GraphInterface::vertex_t GetDescriptor() const
{
return _v;
......@@ -127,7 +118,6 @@ public:
template <class DegSelector>
struct get_degree
{
template<class Graph>
void operator()(const Graph& g,
typename boost::graph_traits<Graph>::vertex_descriptor v,
size_t& deg) const
......@@ -135,120 +125,100 @@ public:
deg = DegSelector()(v, g);
}
template<class Graph, class PMap>
template<class PMap>
void operator()(const Graph& g,
typename boost::graph_traits<Graph>::vertex_descriptor v,
const PMap& weight,
boost::python::object& deg) const
const boost::any& aweight, boost::python::object& deg,
bool& found, PMap) const
{
deg = boost::python::object(DegSelector()(v, g, weight));
try
{
const PMap& weight = boost::any_cast<const PMap&>(aweight);
deg = boost::python::object(DegSelector()(v, g, weight));
found = true;
}
catch (boost::bad_any_cast&) {}
}
};
size_t GetInDegree() const
{
CheckValid();
GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
std::shared_ptr<Graph> gp(_g);
Graph& g = *gp.get();
size_t in_deg;
run_action<>()(gi, std::bind(get_degree<in_degreeS>(),
std::placeholders::_1, _v,
std::ref(in_deg)))();
get_degree<in_degreeS>()(g, _v, in_deg);
return in_deg;
}
boost::python::object GetWeightedInDegree(boost::any pmap) const
{
if (!belongs<edge_scalar_properties>()(pmap))
throw ValueException("edge weight property must be of scalar type");
CheckValid();
GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
std::shared_ptr<Graph> gp(_g);
Graph& g = *gp.get();
boost::python::object in_deg;
run_action<>()(gi, std::bind(get_degree<in_degreeS>(),
std::placeholders::_1, _v,
std::placeholders::_2,
std::ref(in_deg)),
edge_scalar_properties())(pmap);
bool found = false;
boost::mpl::for_each<edge_scalar_properties>(std::bind(get_degree<in_degreeS>(),
std::ref(g), _v,
std::ref(pmap),
std::ref(in_deg),
std::ref(found),
std::placeholders::_1));
if (!found)
throw ValueException("edge weight property must be of scalar type");
return in_deg;
}
size_t GetOutDegree() const
{
CheckValid();
GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
std::shared_ptr<Graph> gp(_g);
Graph& g = *gp.get();
size_t out_deg;
run_action<>()(gi, std::bind(get_degree<out_degreeS>(),
std::placeholders::_1, _v,
std::ref(out_deg)))();
get_degree<out_degreeS>()(g, _v, out_deg);
return out_deg;
}
boost::python::object GetWeightedOutDegree(boost::any pmap) const
{
if (!belongs<edge_scalar_properties>()(pmap))
throw ValueException("edge weight property must be of scalar type");
CheckValid();
GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
std::shared_ptr<Graph> gp(_g);
Graph& g = *gp.get();
boost::python::object out_deg;
run_action<>()(gi, std::bind(get_degree<out_degreeS>(),
std::placeholders::_1, _v,
std::placeholders::_2,
std::ref(out_deg)),
edge_scalar_properties())(pmap);
bool found = false;
boost::mpl::for_each<edge_scalar_properties>(std::bind(get_degree<out_degreeS>(),
std::ref(g), _v,
std::ref(pmap),
std::ref(out_deg),
std::ref(found),
std::placeholders::_1));
if (!found)
throw ValueException("edge weight property must be of scalar type");
return out_deg;
}
// provide iterator support for out_edges
struct get_out_edges
{
template<class Graph>
void operator()(const Graph& g, const boost::python::object& pg,
typename boost::graph_traits<Graph>::vertex_descriptor v,
boost::python::object& iter) const
{
typedef typename boost::graph_traits<Graph>::out_edge_iterator
out_edge_iterator;
iter = boost::python::object(PythonIterator<PythonEdge<Graph>,
out_edge_iterator>
(pg, out_edges(v, g)));
}
};
boost::python::object OutEdges() const
{
CheckValid();
GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
boost::python::object iter;
run_action<>()(gi, std::bind(get_out_edges(), std::placeholders::_1,
std::ref(_g), _v, std::ref(iter)))();
return iter;
std::shared_ptr<Graph> pg(_g);
Graph& g = *pg;
typedef typename boost::graph_traits<Graph>::out_edge_iterator
out_edge_iterator;
return boost::python::object(PythonIterator<Graph,PythonEdge<Graph>,
out_edge_iterator>
(pg, out_edges(_v, g)));
}
struct get_in_edges
{
template<class Graph>
void operator()(const Graph& g, const boost::python::object& pg,
typename boost::graph_traits<Graph>::vertex_descriptor v,
boost::python::object& iter) const
{
typedef typename in_edge_iteratorS<Graph>::type
in_edge_iterator;
iter = boost::python::object
(PythonIterator<PythonEdge<Graph>,in_edge_iterator>
(pg, in_edge_iteratorS<Graph>::get_edges(v, g)));
}
};
boost::python::object InEdges() const
{
CheckValid();
GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
boost::python::object iter;
run_action<>()(gi, std::bind(get_in_edges(), placeholders::_1,
std::ref(_g), _v,
std::ref(iter)))();
return iter;
std::shared_ptr<Graph> pg(_g);
Graph& g = *pg;
typedef typename in_edge_iteratorS<Graph>::type
in_edge_iterator;
return boost::python::object(PythonIterator<Graph, PythonEdge<Graph>,
in_edge_iterator>
(pg, in_edge_iteratorS<Graph>::get_edges(_v, g)));
}
std::string GetString() const
......@@ -267,10 +237,34 @@ public:
return _v;
}
size_t GetGraphPtr() const
{
std::shared_ptr<Graph> pg(_g);
return size_t(pg.get());
}
std::string GetGraphType() const
{
using boost::python::detail::gcc_demangle;
return gcc_demangle(typeid(Graph).name());
}
template <class OGraph>
bool operator==(const PythonVertex<OGraph>& other) const { return _v == other._v; }
template <class OGraph>
bool operator!=(const PythonVertex<OGraph>& other) const { return _v != other._v; }
template <class OGraph>
bool operator<(const PythonVertex<OGraph>& other) const { return _v < other._v; }
template <class OGraph>
bool operator<=(const PythonVertex<OGraph>& other) const { return _v <= other._v; }
template <class OGraph>
bool operator>(const PythonVertex<OGraph>& other) const { return _v > other._v; }
template <class OGraph>
bool operator>=(const PythonVertex<OGraph>& other) const { return _v >= other._v; }
private:
boost::python::object _g;
std::weak_ptr<Graph> _g;
GraphInterface::vertex_t _v;
bool _valid;
};
// below are classes related to the PythonEdge type
......@@ -282,35 +276,23 @@ class PythonEdge : public EdgeBase
{
public:
typedef typename boost::graph_traits<Graph>::edge_descriptor edge_descriptor;
PythonEdge(const boost::python::object& g, edge_descriptor e)
: _g(g), _e(e), _valid(true)
{
}
PythonEdge(std::shared_ptr<Graph> g, edge_descriptor e)
: _g(g), _e(e) {}
bool IsValid() const
{
boost::python::object g = _g();
if (g.ptr() == Py_None || !_valid)
std::shared_ptr<Graph> gp(_g);
Graph* g = gp.get();
if (g == nullptr)
return false;
GraphInterface& gi = boost::python::extract<GraphInterface&>(g.attr("_Graph__graph"));
GraphInterface::edge_t e(_e);
typename GraphInterface::multigraph_t::vertex_t s, t;
s = source(e, *gi._mg);
t = target(e, *gi._mg);
bool valid = ((s != boost::graph_traits<GraphInterface::multigraph_t>::null_vertex()) &&
(s < num_vertices(*gi._mg)) &&
(t != boost::graph_traits<GraphInterface::multigraph_t>::null_vertex()) &&
(t < num_vertices(*gi._mg)) &&
gi.GetEdgeIndex()[e] <= gi.GetMaxEdgeIndex());
auto s = source(_e, *g);
auto t = target(_e, *g);
return valid;
}
void SetValid(bool valid)
{
_valid = valid;
return ((s != boost::graph_traits<Graph>::null_vertex()) &&
(s < num_vertices(*g)) &&
(t != boost::graph_traits<Graph>::null_vertex()) &&
(t < num_vertices(*g)));
}
void CheckValid() const
......@@ -319,79 +301,95 @@ public:
throw ValueException("invalid edge descriptor");
}
boost::python::object GetGraph() const
{
return _g();
}
GraphInterface::edge_t GetDescriptor() const
{
return _e;
}
struct get_source
PythonVertex<Graph> GetSource() const
{
template<class GraphType>
void operator()(const GraphType& g, const boost::python::object& pg,
const edge_descriptor& edge, boost::python::object& vertex)
const
{
typedef typename boost::graph_traits<GraphType>::edge_descriptor edge_t;
vertex = boost::python::object(PythonVertex(pg, source(edge_t(edge), g)));
}
};
CheckValid();
std::shared_ptr<Graph> pg(_g);
Graph& g = *pg;
return PythonVertex<Graph>(pg, source(_e, g));
}
boost::python::object GetSource() const
PythonVertex<Graph> GetTarget() const
{
CheckValid();
GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
boost::python::object v;
run_action<>()(gi, std::bind(get_source(), std::placeholders::_1,
std::ref(_g), std::ref(_e),
std::ref(v)))();
return v;
std::shared_ptr<Graph> pg(_g);
Graph& g = *pg;
return PythonVertex<Graph>(pg, target(_e, g));
}
struct get_target
std::string GetString() const
{
template<class GraphType>
void operator()(const GraphType& g, const boost::python::object& pg,
const edge_descriptor& edge, boost::python::object& vertex)