Commit 72bd25b3 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement GraphView class

This implements a GraphView class which allows for convenient,
independent filtering graphs.
parent 15927c58
......@@ -31,13 +31,11 @@ using namespace graph_tool;
// this is the constructor for the graph interface
GraphInterface::GraphInterface()
:_mg(),
_nedges(0),
:_state(new state_t()),
_vertex_index(get(vertex_index, _state->_mg)),
_edge_index(get(edge_index_t(), _state->_mg)),
_reversed(false),
_directed(true),
_vertex_index(get(vertex_index,_mg)),
_edge_index(get(edge_index_t(),_mg)),
_max_edge_index(0),
_graph_index(0),
_vertex_filter_map(_vertex_index),
_vertex_filter_invert(false),
......@@ -46,7 +44,7 @@ GraphInterface::GraphInterface()
_edge_filter_invert(false),
_edge_filter_active(false)
{
_state->_nedges = _state->_max_edge_index = 0;
}
// the destructor
......@@ -79,19 +77,25 @@ size_t GraphInterface::GetNumberOfEdges()
run_action<>()(*this, lambda::var(n) =
lambda::bind<size_t>(HardNumEdges(),lambda::_1))();
else
n = _nedges;
n = _state->_nedges;
return n;
}
void GraphInterface::Clear()
{
_mg.clear();
_state->_mg.clear();
_state->_nedges = 0;
_state->_max_edge_index = 0;
_state->_free_indexes.clear();
}
void GraphInterface::ClearEdges()
{
graph_traits<multigraph_t>::vertex_iterator v, v_end;
for (tie(v, v_end) = vertices(_mg); v != v_end; ++v)
clear_vertex(*v, _mg);
for (tie(v, v_end) = vertices(_state->_mg); v != v_end; ++v)
clear_vertex(*v, _state->_mg);
_state->_nedges = 0;
_state->_max_edge_index = 0;
_state->_free_indexes.clear();
}
......@@ -54,7 +54,7 @@ class GraphInterface
{
public:
GraphInterface();
GraphInterface(const GraphInterface& gi);
GraphInterface(const GraphInterface& g, bool keep_ref);
~GraphInterface();
// useful enums
......@@ -136,10 +136,10 @@ public:
// internal access
multigraph_t& GetGraph() {return _mg;}
multigraph_t& GetGraph() {return _state->_mg;}
vertex_index_map_t GetVertexIndex() {return _vertex_index;}
edge_index_map_t GetEdgeIndex() {return _edge_index;}
size_t GetMaxEdgeIndex(){return _max_edge_index;}
size_t GetMaxEdgeIndex(){return _state->_max_edge_index;}
graph_index_map_t GetGraphIndex() {return graph_index_map_t(0);}
......@@ -162,29 +162,35 @@ private:
template <class Graph>
friend class PythonEdge;
// this is the main graph
multigraph_t _mg;
struct state_t
{
// this is the main graph
multigraph_t _mg;
// keep track of the number of edges, since num_edges() is O(V) in
// adjacency_list... :-(
size_t _nedges;
// keep track of the number of edges, since num_edges() is O(V) in
// adjacency_list... :-(
size_t _nedges;
// this will hold an instance of the graph views at run time
vector<boost::any> _graph_views;
deque<size_t> _free_indexes; // indexes of deleted edges to be used up
// for new edges to avoid very large
// indexes, and property map memory usage
size_t _max_edge_index;
};
// reverse and directed states
bool _reversed;
bool _directed;
shared_ptr<state_t> _state;
// vertex index map
vertex_index_map_t _vertex_index;
// edge index map
edge_index_map_t _edge_index;
deque<size_t> _free_indexes; // indexes of deleted edges to be used up for
// new edges to avoid very large indexes, and
// property map memory usage
size_t _max_edge_index;
// this will hold an instance of the graph views at run time
vector<boost::any> _graph_views;
// reverse and directed states
bool _reversed;
bool _directed;
// graph index map
graph_index_map_t _graph_index;
......@@ -197,7 +203,8 @@ private:
bool _vertex_filter_active;
// edge filter
typedef unchecked_vector_property_map<uint8_t,edge_index_map_t> edge_filter_t;
typedef unchecked_vector_property_map<uint8_t,edge_index_map_t>
edge_filter_t;
edge_filter_t _edge_filter_map;
bool _edge_filter_invert;
bool _edge_filter_active;
......
......@@ -344,7 +344,7 @@ BOOST_PYTHON_MODULE(libgraph_tool_core)
mpl::for_each<mpl::push_back<scalar_types,string>::type>(export_vector_types());
class_<GraphInterface>("GraphInterface", init<>())
.def(init<GraphInterface>())
.def(init<GraphInterface,bool>())
.def("GetNumberOfVertices", &GraphInterface::GetNumberOfVertices)
.def("GetNumberOfEdges", &GraphInterface::GetNumberOfEdges)
.def("SetDirected", &GraphInterface::SetDirected)
......
......@@ -61,15 +61,12 @@ struct graph_copy
};
// copy constructor
GraphInterface::GraphInterface(const GraphInterface& gi)
:_mg(),
_nedges(gi._nedges),
GraphInterface::GraphInterface(const GraphInterface& gi, bool keep_ref)
:_state(keep_ref ? gi._state : shared_ptr<state_t>(new state_t())),
_vertex_index(get(vertex_index, _state->_mg)),
_edge_index(get(edge_index_t(), _state->_mg)),
_reversed(gi._reversed),
_directed(gi._directed),
_vertex_index(get(vertex_index,_mg)),
_edge_index(get(edge_index_t(),_mg)),
_free_indexes(gi._free_indexes),
_max_edge_index(gi._max_edge_index),
_vertex_filter_map(_vertex_index),
_vertex_filter_invert(false),
_vertex_filter_active(false),
......@@ -77,10 +74,11 @@ GraphInterface::GraphInterface(const GraphInterface& gi)
_edge_filter_invert(false),
_edge_filter_active(false)
{
graph_copy()(_mg, gi._mg, _vertex_index,
gi._vertex_index, _edge_index,
gi._edge_index);
if (keep_ref)
return;
_state->_nedges = _state->_max_edge_index = 0;
graph_copy()(_state->_mg, gi._state->_mg, _vertex_index, gi._vertex_index,
_edge_index, gi._edge_index);
// filters will be copied in python
}
......@@ -270,7 +268,7 @@ void GraphInterface::CopyVertexProperty(const GraphInterface& src,
run_action<unfiltered>()
(*this, bind<void>(copy_property<vertex_selector>(),
_1, ref(src._mg), _2, _3),
_1, ref(src._state->_mg), _2, _3),
vertex_properties(), writable_vertex_properties())
(prop_src, prop_tgt);
}
......@@ -283,7 +281,7 @@ void GraphInterface::CopyEdgeProperty(const GraphInterface& src,
run_action<unfiltered>()
(*this, bind<void>(copy_property<edge_selector>(),
_1, ref(src._mg), _2, _3),
_1, ref(src._state->_mg), _2, _3),
edge_properties(), writable_edge_properties())
(prop_src, prop_tgt);
}
......@@ -189,11 +189,12 @@ boost::any GraphInterface::GetGraphView() const
// TODO: implement memoization
boost::any graph =
check_filtered(_mg, _edge_filter_map, _edge_filter_invert,
_edge_filter_active, _max_edge_index, _vertex_filter_map,
_vertex_filter_invert, _vertex_filter_active,
const_cast<vector<boost::any>&>(_graph_views),
_reversed, _directed);
check_filtered(_state->_mg, _edge_filter_map, _edge_filter_invert,
_edge_filter_active, _state->_max_edge_index,
_vertex_filter_map, _vertex_filter_invert,
_vertex_filter_active,
const_cast<vector<boost::any>&>(_graph_views), _reversed,
_directed);
return graph;
}
......@@ -211,11 +212,11 @@ void GraphInterface::ReIndexEdges()
{
size_t index = 0;
graph_traits<multigraph_t>::edge_iterator e, e_end;
for (tie(e, e_end) = edges( _mg); e != e_end; ++e)
for (tie(e, e_end) = edges(_state->_mg); e != e_end; ++e)
_edge_index[*e] = index++;
_max_edge_index = (index > 0) ? index - 1 : 0;
_nedges = index;
_free_indexes.clear();
_state->_max_edge_index = (index > 0) ? index - 1 : 0;
_state->_nedges = index;
_state->_free_indexes.clear();
}
// this will definitively remove all the edges from the graph, which are being
......@@ -229,9 +230,9 @@ void GraphInterface::PurgeEdges()
graph_traits<multigraph_t>::vertex_iterator v, v_end;
graph_traits<multigraph_t>::out_edge_iterator e, e_end;
vector<graph_traits<multigraph_t>::edge_descriptor> deleted_edges;
for (tie(v, v_end) = vertices(_mg); v != v_end; ++v)
for (tie(v, v_end) = vertices(_state->_mg); v != v_end; ++v)
{
for (tie(e, e_end) = out_edges(*v, _mg); e != e_end; ++e)
for (tie(e, e_end) = out_edges(*v, _state->_mg); e != e_end; ++e)
if (!filter(*e))
deleted_edges.push_back(*e);
for (typeof(deleted_edges.begin()) iter = deleted_edges.begin();
......@@ -251,10 +252,10 @@ void GraphInterface::PurgeVertices()
MaskFilter<vertex_filter_t> filter(_vertex_filter_map,
_vertex_filter_invert);
size_t N = num_vertices(_mg);
size_t N = num_vertices(_state->_mg);
vector<bool> deleted(N, false);
for (size_t i = 0; i < N; ++i)
deleted[i] = !filter(vertex(i, _mg));
deleted[i] = !filter(vertex(i, _state->_mg));
vector<graph_traits<multigraph_t>::edge_descriptor> edges;
......@@ -263,17 +264,18 @@ void GraphInterface::PurgeVertices()
{
if (deleted[i])
{
graph_traits<multigraph_t>::vertex_descriptor v = vertex(i, _mg);
graph_traits<multigraph_t>::vertex_descriptor v =
vertex(i, _state->_mg);
graph_traits<multigraph_t>::out_edge_iterator e, e_end;
for(tie(e, e_end) = out_edges(v, _mg); e != e_end; ++e)
for(tie(e, e_end) = out_edges(v, _state->_mg); e != e_end; ++e)
edges.push_back(*e);
graph_traits<multigraph_t>::in_edge_iterator ei, ei_end;
for(tie(ei, ei_end) = in_edges(v, _mg); ei != ei_end; ++ei)
for(tie(ei, ei_end) = in_edges(v, _state->_mg); ei != ei_end; ++ei)
edges.push_back(*ei);
for(size_t j = 0; j < edges.size(); ++j)
RemoveEdgeIndex(edges[j]);
clear_vertex(v, _mg);
remove_vertex(v, _mg);
clear_vertex(v, _state->_mg);
remove_vertex(v, _state->_mg);
edges.clear();
}
}
......
......@@ -599,7 +599,8 @@ struct graph_action
mpl::transform<GraphViews, mpl::quote1<add_pointer> >::type {};
graph_action(GraphInterface& g, Action a)
: _g(g), _a(a, g, num_vertices(g._mg), g._max_edge_index + 1) {}
: _g(g), _a(a, g, num_vertices(g._state->_mg),
g._state->_max_edge_index + 1) {}
void operator()() const
{
......
......@@ -32,6 +32,7 @@
#include <boost/graph/graphviz.hpp>
#include <boost/python/extract.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/xpressive/xpressive.hpp>
#include "graph_python_interface.hh"
......@@ -509,9 +510,10 @@ python::tuple GraphInterface::ReadFromFile(string file, python::object pfile,
create_dynamic_map<vertex_index_map_t,edge_index_map_t>
map_creator(_vertex_index, _edge_index);
dynamic_properties dp(map_creator);
_mg.clear();
_state->_mg.clear();
GraphEdgeIndexWrap<multigraph_t,edge_index_map_t> wg(_mg, _edge_index);
GraphEdgeIndexWrap<multigraph_t,edge_index_map_t> wg(_state->_mg,
_edge_index);
_directed = true;
try
{
......@@ -537,8 +539,9 @@ python::tuple GraphInterface::ReadFromFile(string file, python::object pfile,
else
read_graphml(stream, ug, dp);
}
_nedges = num_edges(_mg);
_max_edge_index = (_nedges > 0) ? _nedges - 1 : 0;
_state->_nedges = num_edges(_state->_mg);
_state->_max_edge_index = (_state->_nedges > 0) ?
_state->_nedges - 1 : 0;
python::dict vprops, eprops, gprops;
for(typeof(dp.begin()) iter = dp.begin(); iter != dp.end(); ++iter)
......
......@@ -62,7 +62,7 @@ void GraphInterface::ShiftVertexProperty(boost::any prop, size_t index) const
{
bool found = false;
mpl::for_each<writable_vertex_properties>
(bind<void>(shift_vertex_property(), _1, ref(_mg),
(bind<void>(shift_vertex_property(), _1, ref(_state->_mg),
prop, index, ref(found)));
if (!found)
throw GraphException("invalid writable property map");
......
......@@ -168,17 +168,17 @@ struct add_new_edge
void GraphInterface::AddEdgeIndex(const edge_t& e)
{
if (!_free_indexes.empty())
if (!_state->_free_indexes.empty())
{
_edge_index[e] = _free_indexes.front();
_free_indexes.pop_front();
_edge_index[e] = _state->_free_indexes.front();
_state->_free_indexes.pop_front();
}
else
{
_edge_index[e] = _nedges;
_max_edge_index = _nedges;
_edge_index[e] = _state->_nedges;
_state->_max_edge_index = _state->_nedges;
}
_nedges++;
_state->_nedges++;
}
python::object add_edge(python::object g, const python::object& s,
......@@ -230,27 +230,28 @@ void remove_edge(GraphInterface& gi, const python::object& e)
void GraphInterface::RemoveEdgeIndex(const edge_t& e)
{
size_t index = _edge_index[e];
if (index == _max_edge_index)
if (index == _state->_max_edge_index)
{
if (_max_edge_index > 0)
_max_edge_index--;
if (_state->_max_edge_index > 0)
_state->_max_edge_index--;
while (!_free_indexes.empty() &&
_max_edge_index == _free_indexes.back())
while (!_state->_free_indexes.empty() &&
_state->_max_edge_index == _state->_free_indexes.back())
{
_free_indexes.pop_back();
if (_max_edge_index > 0)
_max_edge_index--;
_state->_free_indexes.pop_back();
if (_state->_max_edge_index > 0)
_state->_max_edge_index--;
}
}
else
{
typeof(_free_indexes.begin()) pos =
lower_bound(_free_indexes.begin(), _free_indexes.end(), index);
_free_indexes.insert(pos, index);
typeof(_state->_free_indexes.begin()) pos =
lower_bound(_state->_free_indexes.begin(),
_state->_free_indexes.end(), index);
_state->_free_indexes.insert(pos, index);
}
_nedges--;
remove_edge(e, _mg);
_state->_nedges--;
remove_edge(e, _state->_mg);
}
struct get_degree_map
......@@ -277,7 +278,7 @@ python::object GraphInterface::DegreeMap(string deg) const
map_t;
map_t deg_map(_vertex_index);
deg_map.reserve(num_vertices(_mg));
deg_map.reserve(num_vertices(_state->_mg));
if (deg == "in")
run_action<>()(const_cast<GraphInterface&>(*this),
......
......@@ -87,7 +87,7 @@ public:
GraphInterface& gi = python::extract<GraphInterface&>(_g());
return _valid &&
(_v != graph_traits<GraphInterface::multigraph_t>::null_vertex()) &&
(_v < num_vertices(gi._mg));
(_v < num_vertices(gi._state->_mg));
}
void SetValid(bool valid)
......@@ -251,8 +251,9 @@ public:
return false;
GraphInterface& gi = python::extract<GraphInterface&>(_g());
GraphInterface::edge_t e(_e);
return (_valid && PythonVertex(_g, source(e, gi._mg)).IsValid() &&
PythonVertex(_g, target(e, gi._mg)).IsValid());
return (_valid &&
PythonVertex(_g, source(e, gi._state->_mg)).IsValid() &&
PythonVertex(_g, target(e, gi._state->_mg)).IsValid());
}
void SetValid(bool valid)
......
......@@ -26,7 +26,6 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/xpressive/xpressive.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <string>
......
......@@ -59,15 +59,16 @@ Summary
:nosignatures:
Graph
GraphView
Vertex
Edge
PropertyMap
PropertyArray
load_graph
group_vector_property
ungroup_vector_property
value_types
show_config
PropertyArray
Classes
-------
......@@ -88,11 +89,11 @@ import scipy.stats
from . core import __version__, Graph, Vector_bool, Vector_int32_t, \
Vector_int64_t, Vector_double, Vector_long_double, Vector_string, \
value_types, load_graph, PropertyArray, PropertyMap, Vertex, Edge, \
group_vector_property, ungroup_vector_property, show_config
__all__ = ["Graph", "Vertex", "Edge", "Vector_bool", "Vector_int32_t",
"Vector_int64_t", "Vector_double", "Vector_long_double",
"Vector_string", "value_types", "load_graph", "PropertyMap",
"group_vector_property", "ungroup_vector_property", "show_config",
"PropertyArray", "__author__", "__copyright__", "__URL__",
"__version__"]
group_vector_property, ungroup_vector_property, show_config, GraphView
__all__ = ["Graph", "GraphView", "Vertex", "Edge", "Vector_bool",
"Vector_int32_t", "Vector_int64_t", "Vector_double",
"Vector_long_double", "Vector_string", "value_types", "load_graph",
"PropertyMap", "group_vector_property", "ungroup_vector_property",
"show_config", "PropertyArray", "__author__", "__copyright__",
"__URL__", "__version__"]
......@@ -593,7 +593,7 @@ class Graph(object):
self.__graph = libcore.GraphInterface()
self.set_directed(directed)
else:
self.__graph = libcore.GraphInterface(g.__graph)
self.__graph = libcore.GraphInterface(g.__graph, False)
for k, v in g.__properties.iteritems():
new_p = self.new_property(v.key_type(), v.value_type())
self.copy_property(v, new_p, g)
......@@ -652,9 +652,10 @@ class Graph(object):
f += ", vertices filtered by %s" % (str(self.get_vertex_filter()))
n = self.num_vertices()
e = self.num_edges()
return "<Graph object, %s%s, with %d %s and %d edge%s%s at 0x%x>"\
% (d, fr, n, "vertex" if n == 1 else "vertices", e,
"" if e == 1 else "s", f, id(self))
return "<%s object, %s%s, with %d %s and %d edge%s%s at 0x%x>"\
% (type(self).__name__, d, fr, n,
"vertex" if n == 1 else "vertices", e, "" if e == 1 else "s",
f, id(self))
# Graph access
# ============
......@@ -1347,3 +1348,87 @@ for vt in vector_types:
vt.a = property(_get_array_view)
Vector_string.a = None
Vector_string.get_array = lambda self: None
class GraphView(Graph):
"""
A view of selected vertices or edges of another graph.
This class uses shared data from another :class:`~graph_tool.Graph`
instance, but allows for local filtering of vertices and/or edges, edge
directionality or reversal.
The existence of a :class:`~graph_tool.GraphView` object does not affect the
original graph, except if the graph view is modified (addition or removal of
vertices or edges), in which case the modification is directly reflected in
the original graph (and vice-versa), since they both point to the same
underlying data. Because of this, instances of
:class:`~graph_tool.PropertyMap` can be used interchangeably with a graph
and its views.
The argument ``g`` must be an instance of a :class:`~graph_tool.Graph`
class. If specified, ``vfilt`` and ``efilt`` select which vertices and edges
are filtered, respectively. These parameters can either be a
boolean-valued :class:`~graph_tool.PropertyMap` or a
:class:`~numpy.ndarray`, which specify which vertices/edges are selected, or
an unary function which returns ``True`` if a given vertex/edge is to be
selected, or ``False`` otherwise.
The boolean parameter ``directed`` can be used to set the directionality of
the graph view. If ``directed = None``, the directionality is inherited from
``g``.
If ``reversed = True``, the direction of the edges is reversed.
If ``vfilt`` or ``efilt`` is anything other than a
:class:`~graph_tool.PropertyMap` instance, the instantiation running time is
:math:`O(V)` and :math:`O(E)`, respectively. Otherwise, the running time is
:math:`O(1)`.
"""
def __init__(self, g, vfilt=None, efilt=None, directed=None,
reversed=False):
Graph.__init__(self)
self._Graph__graph = libcore.GraphInterface(g._Graph__graph, True)
self._Graph__properties = g._Graph__properties
self._Graph__known_properties = g._Graph__known_properties
self.__parent = g
if vfilt is not None:
if type(vfilt) is PropertyMap:
self.set_vertex_filter(vfilt)
else:
vmap = self.new_vertex_property("bool")
if issubclass(type(vfilt), numpy.ndarray):
vmap.a = vfilt
else:
omap, inv = g.get_vertex_filter()
if omap is not None:
vmap.a = omap.a if not inv else omap.a ^ 1
for v in g.vertices():
vmap[v] = vfilt(v)
self.set_vertex_filter(vmap)
if efilt is not None:
if type(efilt) is PropertyMap:
self.set_edge_filter(efilt)
else:
emap = self.new_edge_property("bool")
if issubclass(type(efilt), numpy.ndarray):
vmap.a = efilt
else:
omap, inv = g.get_edge_filter()
if omap is not None:
emap.a = omap.a if not inv else omap.a ^ 1
for e in g.edges():
emap[e] = efilt(e)
self.set_edge_filter(emap)
if directed is not None:
self.set_directed(directed)
if reversed:
self.set_reversed(not g.is_reversed())
def get_parent(self):
"Return parent graph."
return self.__parent
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment