// graph-tool -- a general graph modification and manipulation thingy // // Copyright (C) 2007-2012 Tiago de Paula Peixoto // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef FILTERING_HH #define FILTERING_HH #include #include #include #if (BOOST_VERSION / 100 % 1000 >= 48) #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "graph.hh" #include "graph_adaptor.hh" #include "graph_selectors.hh" #include "graph_util.hh" #include "graph_wrap.hh" #include "mpl_nested_loop.hh" namespace graph_tool { using namespace boost; // Graph filtering // --------------- // // We want to generate versions of a template algorithm for every possible type // of graph views. The types of graph views are the following: // // - 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. // // - 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 specific specialization can be called at run time (and generated at // compile time) with the run_action() function, which takes as arguments the // GraphInterface worked on, and the template functor to be specialized, which // must take as first argument a pointer to a graph type, which itself must be a // template parameter. Additionally, the function can be called optionally with // up to 4 extra arguments, which must be MPL sequence instances corresponding // to the type range of the extra arguments which must be passed to the template // functor. The run_action() will not run the algorithm itself, but will instead // return a functor (graph_action) which must be called either with no // arguments, or with parameters of type boost::any which hold internally the // type and values of the extra parameters to be passed to the action functor, // and which will define the specialization to be chosen. // // Example: // // struct my_algorithm // { // template // void operator()(Graph& g, ValueType val) const // { // ... do something ... // } // }; // // ... // // GraphInterface g; // typedef mpl::vector value_types; // double foo = 42.0; // run_action(g, my_algorithm(), value_types)(boost::any(foo)); // // The above line will run my_algorithm::operator() with Graph being the // appropriate graph view type and ValueType being 'double' and val = 42.0. // Whenever no implementation is called, the following exception is thrown class ActionNotFound: public GraphException { public: ActionNotFound(const boost::any& graph_view, const std::type_info& action, const vector& args); virtual const char * what () const throw (); virtual ~ActionNotFound() throw () {} private: boost::any _graph_view; const std::type_info& _action; vector _args; }; namespace detail { // Implementation // -------------- // // The class MaskFilter below is the main filter predicate for the filtered // graph view, based on descriptor property maps. It filters out edges or // vertices which are masked according to a property map with bool (actually // uint8_t) value type. template class MaskFilter { public: typedef typename property_traits::value_type value_t; MaskFilter(){} MaskFilter(DescriptorProperty filtered_property, bool invert) : _filtered_property(filtered_property), _invert(invert) {} template inline bool operator() (Descriptor d) const { // ignore if masked return get(_filtered_property, 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 // through. Therefore, it must be guaranteed this is as optimized // as possible. } private: DescriptorProperty _filtered_property; bool _invert; }; // Metaprogramming // --------------- // // 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 struct get_predicate { typedef MaskFilter type; }; template <> struct get_predicate { typedef keep_all type; }; // metafunction to get the filtered graph type struct graph_filter { template struct apply { typedef typename get_predicate::type edge_predicate; typedef typename get_predicate::type vertex_predicate; typedef filtered_graph filtered_graph_t; // If both predicates are keep_all, then return the original graph // type. Otherwise return the filtered_graph type. typedef typename mpl::if_< typename mpl::and_< is_same, is_same >::type, Graph, filtered_graph_t>::type type; }; }; // metafunction to get the undirected graph type struct graph_undirect { template struct apply { typedef UndirectedAdaptor type; }; }; // metafunction to get the reversed graph type struct graph_reverse { template struct apply { typedef reverse_graph type; }; }; // 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 struct bind_wrap1 { template struct apply { typedef typename Bind::template apply::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 struct apply { typedef mpl::pair type; }; }; template struct apply { struct get_second_types { template struct apply { typedef typename mpl::transform< TR2, bind_wrap1< mpl::bind2 > >::type type; }; }; typedef typename mpl::transform< TR1, get_second_types, mpl::back_inserter > >::type pair_combinations; // nested sequence (vector of vectors) of // pair combinations // joins two sequences struct join { template struct apply { typedef typename boost::mpl::copy< Seq2, boost::mpl::back_inserter >::type type; }; }; // flattens a nested sequence template struct flatten { typedef typename boost::mpl::fold< Sequence, typename boost::mpl::clear::type, join >::type type; }; // the complete list of combinations typedef typename flatten::type type; }; }; // metafunction class to get the correct property map type template struct get_property_map_type { typedef typename property_map_type::apply ::type::unchecked_t type; }; template struct get_property_map_type { typedef keep_all type; }; // this metafunction returns a filtered graph type, given the scalar types to be // used in the property maps struct get_graph_filtered { template 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 mpl::if_< is_same, GraphInterface::edge_index_map_t, typename get_property_map_type< edge_scalar, GraphInterface::edge_index_map_t>::type >::type edge_property_map; typedef typename mpl::if_< is_same, GraphInterface::vertex_index_map_t, typename get_property_map_type< vertex_scalar, GraphInterface::vertex_index_map_t>::type >::type vertex_property_map; typedef typename graph_filter::apply::type type; }; }; // this metafunction returns all the possible graph views struct get_all_graph_views { template , class NeverDirected = mpl::bool_, class AlwaysReversed = mpl::bool_, class NeverReversed = mpl::bool_, class NeverFiltered = mpl::bool_ > struct apply { // filtered graphs struct filtered_graphs: mpl::if_ , typename mpl::transform::type>::type {}; // filtered + reversed graphs struct reversed_graphs: mpl::if_::type, typename mpl::if_< NeverReversed, filtered_graphs, typename mpl::transform< filtered_graphs, graph_reverse, mpl::back_inserter >::type >::type >::type {}; // undirected + filtereed + reversed graphs struct undirected_graphs: mpl::if_::type, typename mpl::transform< filtered_graphs, graph_undirect, mpl::back_inserter >::type >::type >::type {}; typedef undirected_graphs type; }; }; // useful metafunction to split sequences in half struct split { template struct get_element { template struct apply { typedef typename mpl::at::type type; }; }; template struct apply { typedef typename mpl::size::type size; typedef typename mpl::divides >::type half_size; typedef typename mpl::transform, get_element, mpl::back_inserter > > ::type first_part; typedef typename mpl::transform, get_element, mpl::back_inserter > > ::type second_part; typedef typename mpl::pair type; }; }; // all scalar types plus edge and vertex index property (we actually only use // bool) #ifndef NO_GRAPH_FILTERING struct edge_scalars: mpl::vector {}; struct vertex_scalars: mpl::vector {}; #else struct edge_scalars: mpl::vector {}; struct vertex_scalars: mpl::vector {}; #endif // all scalar pairs struct scalar_pairs: get_all_pairs::apply::type {}; // finally, this type should hold all the possible graph views struct all_graph_views: get_all_graph_views::apply::type {}; // restricted graph views struct always_directed: get_all_graph_views::apply >::type {}; struct never_directed: get_all_graph_views::apply, mpl::bool_ >::type {}; struct always_reversed: get_all_graph_views::apply, mpl::bool_,mpl::bool_ >::type {}; struct never_reversed: get_all_graph_views::apply, mpl::bool_,mpl::bool_, mpl::bool_ >::type {}; struct always_directed_never_reversed: get_all_graph_views::apply, mpl::bool_,mpl::bool_, mpl::bool_ >::type {}; struct never_filtered: get_all_graph_views::apply, mpl::bool_,mpl::bool_, mpl::bool_,mpl::bool_ >::type {}; // sanity check typedef mpl::size::type n_views; #ifndef NO_GRAPH_FILTERING BOOST_MPL_ASSERT_RELATION(n_views::value, == , mpl::int_<12>::value); #else BOOST_MPL_ASSERT_RELATION(n_views::value, == , mpl::int_<3>::value); #endif // run_action() implementation // =========================== // wrap action to be called, to deal with property maps, i.e., return version // with no bounds checking. template struct action_wrap { action_wrap(Action a, GraphInterface& g, size_t max_v, size_t max_e) : _a(a), _g(g), _max_v(max_v), _max_e(max_e) {} template checked_vector_property_map uncheck(checked_vector_property_map a, mpl::true_) const { return a; } template unchecked_vector_property_map uncheck(checked_vector_property_map a, mpl::false_) const { return a.get_unchecked(_max_v); } template checked_vector_property_map uncheck(checked_vector_property_map a, mpl::true_) const { return a; } template unchecked_vector_property_map uncheck(checked_vector_property_map a, mpl::false_) const { return a.get_unchecked(_max_e); } template scalarS uncheck(scalarS a, mpl::false_) const { return scalarS(uncheck(a._pmap, mpl::false_())); } //no op template Type uncheck(Type a, DoWrap) const { return a; } template GraphWrap wrap(Graph* g, mpl::true_) const { return graph_wrap(*g, _g); } template Graph& wrap(Graph* g, mpl::false_) const { return *g; } void operator()() const {}; template void operator()(const T1& a1) const { _a(wrap(a1, Wrap())); } template void operator()(const T1& a1, const T2& a2) const { _a(wrap(a1,Wrap()), uncheck(a2, Wrap())); } template void operator()(const T1& a1, const T2& a2, const T3& a3) const { _a(wrap(a1,Wrap()), uncheck(a2, Wrap()), uncheck(a3, Wrap()));} template void operator()(const T1& a1, const T2& a2, const T3& a3, const T4& a4) const { _a(wrap(a1,Wrap()), uncheck(a2, Wrap()), uncheck(a3, Wrap()), uncheck(a4, Wrap())); } template void operator()(const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5) const { _a(wrap(a1,Wrap()), uncheck(a2, Wrap()), uncheck(a3, Wrap()), uncheck(a4, Wrap()), uncheck(a5, Wrap())); } Action _a; reference_wrapper _g; size_t _max_v, _max_e; }; // this functor encapsulates another functor Action, which takes a pointer to a // graph view as first argument template struct graph_action { struct graph_view_pointers: mpl::transform >::type {}; graph_action(GraphInterface& g, Action a) : _g(g), _a(a, g, num_vertices(g._state->_mg), g._state->_max_edge_index + 1) {} void operator()() const { bool found = false; boost::any gview = _g.GetGraphView(); boost::mpl::for_each (boost::mpl::select_types(_a, found, gview)); if (!found) { throw ActionNotFound(gview, typeid(Action), vector()); } } void operator()(boost::any a1) const { bool found = false; boost::any gview = _g.GetGraphView(); boost::mpl::nested_for_each() (boost::mpl::select_types(_a, found, gview, a1)); if (!found) { vector args; args.push_back(&a1.type()); throw ActionNotFound(gview, typeid(Action), args); } } void operator()(boost::any a1, boost::any a2) const { bool found = false; boost::any gview = _g.GetGraphView(); boost::mpl::nested_for_each() (boost::mpl::select_types(_a, found, gview, a1, a2)); if (!found) { vector args; args.push_back(&a1.type()); args.push_back(&a2.type()); throw ActionNotFound(gview, typeid(Action), args); } } void operator()(boost::any a1, boost::any a2, boost::any a3) const { bool found = false; boost::any gview = _g.GetGraphView(); boost::mpl::nested_for_each() (boost::mpl::select_types(_a, found, gview, a1, a2, a3)); if (!found) { vector args; args.push_back(&a1.type()); args.push_back(&a2.type()); args.push_back(&a3.type()); throw ActionNotFound(gview, typeid(Action), args); } } void operator()(boost::any a1, boost::any a2, boost::any a3, boost::any a4) const { bool found = false; boost::any gview = _g.GetGraphView(); boost::mpl::nested_for_each() (boost::mpl::select_types(_a, found, gview, a1, a2, a3,a4)); if (!found) { vector args; args.push_back(&a1.type()); args.push_back(&a2.type()); args.push_back(&a3.type()); args.push_back(&a4.type()); throw ActionNotFound(gview, typeid(Action), args); } } const GraphInterface &_g; action_wrap _a; }; } // details namespace // all definitions of run_action with different arity template struct run_action { template detail::graph_action operator()(GraphInterface &g, Action a) { return detail::graph_action(g, a); } template detail::graph_action operator()(GraphInterface &g, Action a, TR1) { return detail::graph_action(g, a); } template detail::graph_action operator()(GraphInterface &g, Action a, TR1, TR2) { return detail::graph_action(g, a); } template detail::graph_action operator()(GraphInterface &g, Action a, TR1, TR2, TR3) { return detail::graph_action(g, a); } template detail::graph_action operator()(GraphInterface &g, Action a, TR1, TR2, TR3, TR4) { return detail::graph_action(g, a); } }; // returns true if graph filtering was enabled at compile time bool graph_filtering_enabled(); } //graph_tool namespace #endif // FILTERING_HH