Commit 3d2ea9f7 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Force simultaneous filtering of vertex and edges

Now, if either vertex or edge filtering is enabled, simultaneous
filtering is activated. By not considering vertex or edge filtering in
completely independent fashion, the compile time memory requirements
reduced to about 50%.
parent 7a84d0f3
......@@ -141,10 +141,12 @@
reversible. They are also very cheap, and have an :math:`O(1)`
complexity.
.. automethod:: set_filters
.. automethod:: set_vertex_filter
.. automethod:: get_vertex_filter
.. automethod:: set_edge_filter
.. automethod:: get_edge_filter
.. automethod:: clear_filters
.. warning::
......
......@@ -141,7 +141,7 @@ void get_motifs(GraphInterface& g, size_t k, boost::python::list subgraph_list,
GraphInterface sub;
sub.SetDirected(g.GetDirected());
typedef graph_tool::detail::get_all_graph_views::apply
<graph_tool::detail::scalar_pairs,
<graph_tool::detail::filt_scalar_type,
boost::mpl::bool_<false>, boost::mpl::bool_<false>,
boost::mpl::bool_<false>, boost::mpl::bool_<true>,
boost::mpl::bool_<true> >::type gviews;
......
......@@ -53,7 +53,7 @@ void generate_graph(GraphInterface& gi, size_t N, boost::python::object deg_samp
rng_t& rng, bool verbose, bool verify)
{
typedef graph_tool::detail::get_all_graph_views::apply<
graph_tool::detail::scalar_pairs, boost::mpl::bool_<false>,
graph_tool::detail::filt_scalar_type, boost::mpl::bool_<false>,
boost::mpl::bool_<false>, boost::mpl::bool_<false>,
boost::mpl::bool_<true>, boost::mpl::bool_<true> >::type graph_views;
......
......@@ -28,7 +28,7 @@ using namespace graph_tool;
typedef graph_tool::detail::get_all_graph_views
::apply<graph_tool::detail::scalar_pairs, mpl::bool_<false>,
::apply<graph_tool::detail::filt_scalar_type, mpl::bool_<false>,
mpl::bool_<true>,mpl::bool_<false>,
mpl::bool_<true>,mpl::bool_<true> >::type graph_views;
......
......@@ -142,6 +142,9 @@ check_filtered(const Graph &g, const EdgeFilter& edge_filter,
if (e_active)
{
if (!v_active)
throw GraphException("Edge filter is active but vertex filter is not. This is a bug.");
if (max_eindex > 0)
edge_filter.reserve(max_eindex);
if (v_active)
......@@ -170,6 +173,9 @@ check_filtered(const Graph &g, const EdgeFilter& edge_filter,
{
if (v_active)
{
if (!e_active)
throw GraphException("Vertex filter is active but edge filter is not. This is a bug.");
if (num_vertices(g) > 0)
vertex_filter.reserve(num_vertices(g));
typedef filtered_graph<Graph, keep_all,
......
......@@ -67,16 +67,14 @@ namespace graph_tool
//
// - The original directed multigraph
//
// - Filtered graphs, based on MaskFilter below. This amounts to a
// filtered_graph for every combination of filtered and unfiltered vertex
// or edge, i.e., 3.
// - Filtered graphs, based on MaskFilter below
//
// - A reversed view of each directed graph (original + filtered)
//
// - An undirected view of each directed (unreversed) graph (original +
// filtered)
//
// The total number of graph views is then: 3 * 4 = 12
// The total number of graph views is then: 1 + 1 + 2 + 2 = 6
//
// The specific specialization can be called at run time (and generated at
// compile time) with the run_action() function, which takes as arguments the
......@@ -147,11 +145,11 @@ public:
: _filtered_property(filtered_property), _invert(invert) {}
template <class Descriptor>
__attribute__((always_inline)) inline bool operator() (Descriptor d) const
__attribute__((always_inline)) inline bool operator() (Descriptor&& d) const
{
// ignore if masked
return get(_filtered_property, d) ^ _invert;
return get(_filtered_property, std::forward<Descriptor>(d)) ^ _invert;
// TODO: This is a critical section. It will be called for every vertex
// or edge in the graph, every time they're iterated
......@@ -171,6 +169,7 @@ private:
// We need to generate a type sequence with all the filtered graph views, which
// will be called all_graph_views.
// metafunction class to get the correct filter predicate
template <class Property>
struct get_predicate
......@@ -232,81 +231,6 @@ struct graph_reverse
};
};
// this wraps an unary metafunction class Bind into a unary metafunction,
// i.e., it is an identity operation. I'm not sure why it's necessary, but
// using pure unary bind expressions below didn't work for me, and this
// fixed it.
template <class Bind>
struct bind_wrap1
{
template <class T1> struct apply
{ typedef typename Bind::template apply<T1>::type type; };
};
// metafunction which returns a mpl::vector containing all the pair combinations
// of two given type sequences
struct get_all_pairs
{
struct make_pair
{
template <class T1, class T2>
struct apply
{
typedef boost::mpl::pair<T1,T2> type;
};
};
template <class TR1, class TR2>
struct apply
{
struct get_second_types
{
template <class T1>
struct apply
{
typedef typename boost::mpl::transform<
TR2,
bind_wrap1<
boost::mpl::bind2<make_pair, T1, boost::mpl::_1>
> >::type type;
};
};
typedef typename boost::mpl::transform<
TR1,
get_second_types,
boost::mpl::back_inserter<boost::mpl::vector<> >
>::type pair_combinations; // nested sequence (vector of vectors) of
// pair combinations
// joins two sequences
struct join
{
template <class Seq1, class Seq2>
struct apply
{
typedef typename boost::mpl::copy<
Seq2,
boost::mpl::back_inserter<Seq1>
>::type type;
};
};
// flattens a nested sequence
template<class Sequence>
struct flatten
{
typedef typename boost::mpl::fold<
Sequence,
typename boost::mpl::clear<Sequence>::type,
join
>::type type;
};
// the complete list of combinations
typedef typename flatten<pair_combinations>::type type;
};
};
// metafunction class to get the correct property map type
template <class Scalar, class IndexMap>
......@@ -326,31 +250,18 @@ struct get_property_map_type<boost::keep_all, IndexMap>
// used in the property maps
struct get_graph_filtered
{
template <class TypePair>
template <class Scalar>
struct apply
{
typedef typename TypePair::first edge_scalar;
typedef typename TypePair::second vertex_scalar;
// if the 'scalar' is the index map itself, use simply that, otherwise
// get the specific property map
typedef typename boost::mpl::if_<
is_same<edge_scalar,
GraphInterface::edge_index_map_t>,
GraphInterface::edge_index_map_t,
typename get_property_map_type<
edge_scalar,
GraphInterface::edge_index_map_t>::type
>::type edge_property_map;
typedef typename get_property_map_type<
Scalar,
GraphInterface::edge_index_map_t>::type edge_property_map;
typedef typename boost::mpl::if_<
is_same<vertex_scalar,
GraphInterface::vertex_index_map_t>,
GraphInterface::vertex_index_map_t,
typename get_property_map_type<
vertex_scalar,
GraphInterface::vertex_index_map_t>::type
>::type vertex_property_map;
typedef typename get_property_map_type<
Scalar,
GraphInterface::vertex_index_map_t>::type vertex_property_map;
typedef typename graph_filter::apply<GraphInterface::multigraph_t,
edge_property_map,
......@@ -361,7 +272,7 @@ struct get_graph_filtered
// this metafunction returns all the possible graph views
struct get_all_graph_views
{
template <class TypePairs,
template <class FiltType,
class AlwaysDirected = boost::mpl::bool_<false>,
class NeverDirected = boost::mpl::bool_<false>,
class AlwaysReversed = boost::mpl::bool_<false>,
......@@ -371,11 +282,11 @@ struct get_all_graph_views
{
// filtered graphs
struct filtered_graphs:
boost::mpl::if_
<NeverFiltered,
boost::mpl::vector<GraphInterface::multigraph_t>,
typename boost::mpl::transform<TypePairs,
get_graph_filtered>::type>::type {};
boost::mpl::if_<NeverFiltered,
boost::mpl::vector<GraphInterface::multigraph_t>,
boost::mpl::vector<GraphInterface::multigraph_t,
typename get_graph_filtered::apply<FiltType>::type>
>::type {};
// filtered + reversed graphs
struct reversed_graphs:
......@@ -445,59 +356,43 @@ struct split
// all scalar types plus edge and vertex index property (we actually only use
// bool)
#ifndef NO_GRAPH_FILTERING
struct edge_scalars:
boost::mpl::vector<boost::keep_all, uint8_t> {};
struct vertex_scalars:
boost::mpl::vector<boost::keep_all, uint8_t> {};
#else
struct edge_scalars:
boost::mpl::vector<boost::keep_all> {};
struct vertex_scalars:
boost::mpl::vector<boost::keep_all> {};
#endif
// all scalar pairs
struct scalar_pairs: get_all_pairs::apply<edge_scalars,vertex_scalars>::type {};
typedef uint8_t filt_scalar_type;
// finally, this type should hold all the possible graph views
struct all_graph_views:
get_all_graph_views::apply<scalar_pairs>::type {};
get_all_graph_views::apply<filt_scalar_type>::type {};
// restricted graph views
struct always_directed:
get_all_graph_views::apply<scalar_pairs,boost::mpl::bool_<true> >::type {};
get_all_graph_views::apply<filt_scalar_type,boost::mpl::bool_<true> >::type {};
struct never_directed:
get_all_graph_views::apply<scalar_pairs,boost::mpl::bool_<false>,
get_all_graph_views::apply<filt_scalar_type,boost::mpl::bool_<false>,
boost::mpl::bool_<true> >::type {};
struct always_reversed:
get_all_graph_views::apply<scalar_pairs,boost::mpl::bool_<true>,
get_all_graph_views::apply<filt_scalar_type,boost::mpl::bool_<true>,
boost::mpl::bool_<false>,boost::mpl::bool_<true> >::type {};
struct never_reversed:
get_all_graph_views::apply<scalar_pairs,boost::mpl::bool_<false>,
get_all_graph_views::apply<filt_scalar_type,boost::mpl::bool_<false>,
boost::mpl::bool_<false>,boost::mpl::bool_<false>,
boost::mpl::bool_<true> >::type {};
struct always_directed_never_reversed:
get_all_graph_views::apply<scalar_pairs,boost::mpl::bool_<true>,
get_all_graph_views::apply<filt_scalar_type,boost::mpl::bool_<true>,
boost::mpl::bool_<false>,boost::mpl::bool_<false>,
boost::mpl::bool_<true> >::type {};
struct never_filtered:
get_all_graph_views::apply<scalar_pairs,boost::mpl::bool_<false>,
get_all_graph_views::apply<filt_scalar_type,boost::mpl::bool_<false>,
boost::mpl::bool_<false>,boost::mpl::bool_<false>,
boost::mpl::bool_<false>,boost::mpl::bool_<true> >::type {};
// sanity check
typedef boost::mpl::size<all_graph_views>::type n_views;
#ifndef NO_GRAPH_FILTERING
BOOST_MPL_ASSERT_RELATION(n_views::value, == , boost::mpl::int_<12>::value);
BOOST_MPL_ASSERT_RELATION(n_views::value, == , boost::mpl::int_<6>::value);
#else
BOOST_MPL_ASSERT_RELATION(n_views::value, == , boost::mpl::int_<3>::value);
#endif
......
......@@ -99,7 +99,7 @@ struct do_get_radial
vertex_t w = target(*e, g);
new_layer.push_back(w);
if (int(layers.size()) - 1 == level[w])
if (int(layers.size()) - 1 == int(level[w]))
last = false;
}
if (out_degree(v, g) == 0)
......
......@@ -178,8 +178,7 @@ void get_dists(GraphInterface& gi, size_t source, int tgt, boost::any dist_map,
(gi, std::bind(do_bfs_search(), placeholders::_1, source, target, gi.GetVertexIndex(),
placeholders::_2, pmap.get_unchecked(num_vertices(gi.GetGraph())),
max_dist),
writable_vertex_scalar_properties(),
mpl::vector<pred_map_t>())
writable_vertex_scalar_properties())
(dist_map);
}
else
......
......@@ -1113,6 +1113,13 @@ class Graph(object):
if g is None:
self.__graph = libcore.GraphInterface()
self.set_directed(directed)
# internal index maps
self.__vertex_index = \
PropertyMap(libcore.get_vertex_index(self.__graph), self, "v")
self.__edge_index = \
PropertyMap(libcore.get_edge_index(self.__graph), self, "e")
else:
if isinstance(prune, bool):
vprune = eprune = rprune = prune
......@@ -1130,12 +1137,18 @@ class Graph(object):
vfilt = g.get_vertex_filter()[0]
efilt = g.get_edge_filter()[0]
if (vorder is None and
((g.get_vertex_filter()[0] is None and g.get_edge_filter()[0] is None) or
(not vprune and not eprune))):
if (vorder is None and ((vfilt is None and efilt is None) or
(not vprune and not eprune))):
# Do a simpler, faster copy.
self.__graph = libcore.GraphInterface(gv.__graph, False,
[], [], None)
# internal index maps
self.__vertex_index = \
PropertyMap(libcore.get_vertex_index(self.__graph), self, "v")
self.__edge_index = \
PropertyMap(libcore.get_edge_index(self.__graph), self, "e")
nvfilt = nefilt = None
for k, m in g.properties.items():
nmap = self.copy_property(m, g=gv)
......@@ -1147,11 +1160,12 @@ class Graph(object):
if vfilt is not None:
if nvfilt is None:
nvfilt = self.copy_property(vfilt, g=gv)
self.set_vertex_filter(nvfilt, g.get_vertex_filter()[1])
if efilt is not None:
if nefilt is None:
nefilt = self.copy_property(efilt, g=gv)
self.set_edge_filter(nefilt, g.get_edge_filter()[1])
self.set_filters(nefilt, nvfilt,
inverted_edges=g.get_edge_filter()[1],
inverted_vertices=g.get_vertex_filter()[1])
else:
# Copy all internal properties from original graph.
......@@ -1187,6 +1201,11 @@ class Graph(object):
vprops,
eprops,
_prop("v", gv, vorder))
# internal index maps
self.__vertex_index = \
PropertyMap(libcore.get_vertex_index(self.__graph), self, "v")
self.__edge_index = \
PropertyMap(libcore.get_edge_index(self.__graph), self, "e")
# Put the copied properties in the internal dictionary
for i, (k, m) in enumerate(gv.vertex_properties.items()):
......@@ -1206,18 +1225,20 @@ class Graph(object):
new_p[self] = v[gv]
self.graph_properties[k] = new_p
epmap = vpmap = None
if vf_pos is not None:
pmap = new_vertex_property("bool",
self.__graph.GetVertexIndex(),
vprops[vf_pos][1])
pmap = PropertyMap(pmap, self, "v")
self.set_vertex_filter(pmap, g.get_vertex_filter()[1])
vpmap = new_vertex_property("bool",
self.__graph.GetVertexIndex(),
vprops[vf_pos][1])
vpmap = PropertyMap(pmap, self, "v")
if ef_pos is not None:
pmap = new_edge_property("bool",
self.__graph.GetEdgeIndex(),
eprops[ef_pos][1])
pmap = PropertyMap(pmap, self, "e")
self.set_edge_filter(pmap, g.get_edge_filter()[1])
epmap = new_edge_property("bool",
self.__graph.GetEdgeIndex(),
eprops[ef_pos][1])
epmap = PropertyMap(pmap, self, "e")
self.set_filters(epmap, vpmap,
inverted_edges=g.get_edge_filter()[1],
inverted_vertices=g.get_vertex_filter()[1])
if not rprune:
self.set_reversed(g.is_reversed())
......@@ -1225,12 +1246,6 @@ class Graph(object):
# directedness is always a filter
self.set_directed(g.is_directed())
# internal index maps
self.__vertex_index = \
PropertyMap(libcore.get_vertex_index(self.__graph), self, "v")
self.__edge_index = \
PropertyMap(libcore.get_edge_index(self.__graph), self, "e")
# modification permissions
self.__perms = {"add_edge": True, "del_edge": True,
"add_vertex": True, "del_vertex": True}
......@@ -1854,16 +1869,57 @@ class Graph(object):
# Filtering
# =========
def set_filters(self, eprop, vprop, inverted_edges=False, inverted_vertices=False):
"""Set the boolean properties for edge and vertex filters, respectively.
Only the vertices and edges with value different than ``True`` are kept in
the filtered graph. If either the ``inverted_edges`` or ``inverted_vertex``
options are supplied with the value ``True``, only the edges or vertices
with value ``False`` are kept. If any of the supplied property is ``None``,
an empty filter is constructed which allows all edges or vertices."""
if eprop is None and vprop is None:
return
if eprop is None:
eprop = self.new_edge_property("bool")
eprop.a = not inverted_edges
if vprop is None:
vprop = self.new_vertex_property("bool")
vprop.a = not inverted_vertices
self.__graph.SetVertexFilterProperty(_prop("v", self, vprop),
inverted_vertices)
self.__filter_state["vertex_filter"] = (vprop, inverted_vertices)
self.__graph.SetEdgeFilterProperty(_prop("e", self, eprop),
inverted_edges)
self.__filter_state["edge_filter"] = (eprop, inverted_edges)
def set_vertex_filter(self, prop, inverted=False):
"""Choose vertex boolean filter property. Only the vertices with value
different than zero are kept in the filtered graph. If the ``inverted``
"""Set the vertex boolean filter property. Only the vertices with value
different than ``False`` are kept in the filtered graph. If the ``inverted``
option is supplied with value ``True``, only the vertices with value
zero are kept. If the supplied property is ``None``, any previous
filtering is removed."""
``False`` are kept. If the supplied property is ``None``, the filter is
replaced by an uniform filter allowing all vertices."""
vfilt = prop
efilt = None
eprop = self.get_edge_filter()
if eprop[0] is None and vfilt is not None:
efilt = self.new_edge_property("bool")
efilt.a = True
if eprop[0] is not None and vfilt is None:
vfilt = self.new_vertex_property("bool")
vfilt.a = not inverted
self.__graph.SetVertexFilterProperty(_prop("v", self, prop),
self.__graph.SetVertexFilterProperty(_prop("v", self, vfilt),
inverted)
self.__filter_state["vertex_filter"] = (prop, inverted)
self.__filter_state["vertex_filter"] = (vfilt, inverted)
if efilt is not None:
self.set_edge_filter(efilt)
def get_vertex_filter(self):
"""Return a tuple with the vertex filter property and bool value
......@@ -1871,19 +1927,42 @@ class Graph(object):
return self.__filter_state["vertex_filter"]
def set_edge_filter(self, prop, inverted=False):
"""Choose edge boolean filter property. Only the edges with value
different than zero are kept in the filtered graph. If the ``inverted``
option is supplied with value ``True``, only the edges with value zero
are kept. If the supplied property is ``None``, any previous filtering
is removed."""
self.__graph.SetEdgeFilterProperty(_prop("e", self, prop), inverted)
self.__filter_state["edge_filter"] = (prop, inverted)
"""Set the edge boolean filter property. Only the edges with value
different than ``False`` are kept in the filtered graph. If the ``inverted``
option is supplied with value ``True``, only the edges with value ``False``
are kept. If the supplied property is ``None``, the filter is
replaced by an uniform filter allowing all edges."""
efilt = prop
vfilt = None
vprop = self.get_vertex_filter()
if vprop[0] is None and efilt is not None:
vfilt = self.new_vertex_property("bool")
vfilt.a = True
if vprop[0] is not None and efilt is None:
efilt = self.new_edge_property("bool")
efilt.a = not inverted
self.__graph.SetEdgeFilterProperty(_prop("e", self, efilt), inverted)
self.__filter_state["edge_filter"] = (efilt, inverted)
if vfilt is not None:
self.set_vertex_filter(vfilt)
def get_edge_filter(self):
"""Return a tuple with the edge filter property and bool value
indicating whether or not it is inverted."""
return self.__filter_state["edge_filter"]
def clear_filters(self):
"""Remove vertex and edge filters, and set the graph to the unfiltered
state."""
self.__graph.SetVertexFilterProperty(_prop("v", self, None), False)
self.__filter_state["vertex_filter"] = (None, False)
self.__graph.SetEdgeFilterProperty(_prop("e", self, None), False)
self.__filter_state["edge_filter"] = (None, False)
def purge_vertices(self, in_place=False):
"""Remove all vertices of the graph which are currently being filtered
out, and return it to the unfiltered state. This operation is not
......
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