source: src/graph/graph_filtering.hh @ 33b912

Revision 33b912, 23.5 KB checked in by Tiago de Paula Peixoto <tiago@…>, 14 months ago (diff)
Fix action_wrap bug for larger number of parameters
  • Property mode set to 100644
Line 
1// graph-tool -- a general graph modification and manipulation thingy
2//
3// Copyright (C) 2007-2011 Tiago de Paula Peixoto <tiago@skewed.de>
4//
5// This program is free software; you can redistribute it and/or
6// modify it under the terms of the GNU General Public License
7// as published by the Free Software Foundation; either version 3
8// of the License, or (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18#ifndef FILTERING_HH
19#define FILTERING_HH
20
21#include <boost/version.hpp>
22#include <boost/graph/graph_traits.hpp>
23#include <boost/graph/filtered_graph.hpp>
24#if (BOOST_VERSION / 100 % 1000 >= 48)
25    #include <boost/graph/reverse_graph_alt.hpp>
26#else
27    #include <boost/graph/reverse_graph.hpp>
28#endif
29#include <boost/mpl/vector.hpp>
30#include <boost/mpl/erase.hpp>
31#include <boost/mpl/clear.hpp>
32#include <boost/mpl/map.hpp>
33#include <boost/mpl/for_each.hpp>
34#include <boost/mpl/at.hpp>
35#include <boost/mpl/or.hpp>
36#include <boost/mpl/if.hpp>
37#include <boost/mpl/logical.hpp>
38#include <boost/mpl/inserter.hpp>
39#include <boost/mpl/insert_range.hpp>
40#include <boost/mpl/assert.hpp>
41#include <boost/mpl/plus.hpp>
42#include <boost/mpl/divides.hpp>
43#include <boost/mpl/arithmetic.hpp>
44#include <boost/mpl/greater_equal.hpp>
45#include <boost/mpl/comparison.hpp>
46#include <boost/mpl/transform_view.hpp>
47#include <boost/mpl/quote.hpp>
48#include <boost/mpl/range_c.hpp>
49
50#include "graph.hh"
51#include "graph_adaptor.hh"
52#include "graph_selectors.hh"
53#include "graph_util.hh"
54#include "graph_wrap.hh"
55#include "mpl_nested_loop.hh"
56
57namespace graph_tool
58{
59
60using namespace boost;
61
62// Graph filtering
63// ---------------
64//
65// We want to generate versions of a template algorithm for every possible type
66// of graph views. The types of graph views are the following:
67//
68//    - The original directed multigraph
69//
70//    - Filtered graphs, based on MaskFilter below. This amounts to a
71//      filtered_graph for every combination of filtered and unfiltered vertex
72//      or edge, i.e., 3.
73//
74//    - A reversed view of each directed graph (original + filtered)
75//
76//    - An undirected view of each directed (unreversed) graph (original +
77//      filtered)
78//
79// The total number of graph views is then: 3 * 4 = 12
80//
81// The specific specialization can be called at run time (and generated at
82// compile time) with the run_action() function, which takes as arguments the
83// GraphInterface worked on, and the template functor to be specialized, which
84// must take as first argument a pointer to a graph type, which itself must be a
85// template parameter. Additionally, the function can be called optionally with
86// up to 4 extra arguments, which must be MPL sequence instances corresponding
87// to the type range of the extra arguments which must be passed to the template
88// functor. The run_action() will not run the algorithm itself, but will instead
89// return a functor (graph_action) which must be called either with no
90// arguments, or with parameters of type boost::any which hold internally the
91// type and values of the extra parameters to be passed to the action functor,
92// and which will define the specialization to be chosen.
93//
94// Example:
95//
96// struct my_algorithm
97// {
98//     template <class Graph, class ValueType>
99//     void operator()(Graph& g, ValueType val) const
100//     {
101//         ... do something ...
102//     }
103// };
104//
105// ...
106//
107// GraphInterface g;
108// typedef mpl::vector<int, double, string> value_types;
109// double foo = 42.0;
110// run_action(g, my_algorithm(), value_types)(boost::any(foo));
111//
112// The above line will run my_algorithm::operator() with Graph being the
113// appropriate graph view type and ValueType being 'double' and val = 42.0.
114
115// Whenever no implementation is called, the following exception is thrown
116class ActionNotFound: public GraphException
117{
118public:
119    ActionNotFound(const boost::any& graph_view, const std::type_info& action,
120                   const vector<const std::type_info*>& args);
121    virtual const char * what () const throw ();
122    virtual ~ActionNotFound() throw () {}
123private:
124    boost::any _graph_view;
125    const std::type_info& _action;
126    vector<const std::type_info*> _args;
127};
128
129namespace detail
130{
131
132// Implementation
133// --------------
134//
135// The class MaskFilter below is the main filter predicate for the filtered
136// graph view, based on descriptor property maps.  It filters out edges or
137// vertices which are masked according to a property map with bool (actually
138// uint8_t) value type.
139
140template <class DescriptorProperty>
141class MaskFilter
142{
143public:
144    typedef typename property_traits<DescriptorProperty>::value_type value_t;
145    MaskFilter(){}
146    MaskFilter(DescriptorProperty filtered_property, bool invert)
147        : _filtered_property(filtered_property), _invert(invert) {}
148
149    template <class Descriptor>
150    inline bool operator() (Descriptor d) const
151    {
152        // ignore if masked
153
154        return get(_filtered_property, d) ^ _invert;
155
156        // TODO: This is a critical section. It will be called for every vertex
157        //       or edge in the graph, every time they're iterated
158        //       through. Therefore, it must be guaranteed this is as optimized
159        //       as possible.
160    }
161
162private:
163    DescriptorProperty _filtered_property;
164    bool _invert;
165};
166
167
168// Metaprogramming
169// ---------------
170//
171// We need to generate a type sequence with all the filtered graph views, which
172// will be called all_graph_views.
173
174// metafunction class to get the correct filter predicate
175template <class Property>
176struct get_predicate
177{
178    typedef MaskFilter<Property> type;
179};
180
181template <>
182struct get_predicate<keep_all>
183{
184    typedef keep_all type;
185};
186
187// metafunction to get the filtered graph type
188struct graph_filter
189{
190    template <class Graph, class EdgeProperty, class VertexProperty>
191    struct apply
192    {
193
194        typedef typename get_predicate<EdgeProperty>::type edge_predicate;
195        typedef typename get_predicate<VertexProperty>::type vertex_predicate;
196
197        typedef filtered_graph<Graph,
198                               edge_predicate,
199                               vertex_predicate> filtered_graph_t;
200
201        // If both predicates are keep_all, then return the original graph
202        // type. Otherwise return the filtered_graph type.
203        typedef typename mpl::if_<
204            typename mpl::and_<
205                is_same<edge_predicate,
206                        keep_all>,
207                is_same<vertex_predicate,
208                        keep_all>
209                >::type,
210            Graph,
211            filtered_graph_t>::type type;
212    };
213};
214
215// metafunction to get the undirected graph type
216struct graph_undirect
217{
218    template <class Graph>
219    struct apply
220    {
221        typedef UndirectedAdaptor<Graph> type;
222    };
223};
224
225// metafunction to get the reversed graph type
226struct graph_reverse
227{
228    template <class Graph>
229    struct apply
230    {
231        typedef reverse_graph<Graph> type;
232    };
233};
234
235// this wraps an unary metafunction class Bind into a unary metafunction,
236// i.e., it is an identity operation. I'm not sure why it's necessary, but
237// using pure unary bind expressions below didn't work for me, and this
238// fixed it.
239template <class Bind>
240struct bind_wrap1
241{
242    template <class T1> struct apply
243    { typedef typename Bind::template apply<T1>::type type; };
244};
245
246// metafunction which returns a mpl::vector containing all the pair combinations
247// of two given type sequences
248struct get_all_pairs
249{
250    struct make_pair
251    {
252        template <class T1, class T2>
253        struct apply
254        {
255            typedef mpl::pair<T1,T2> type;
256        };
257    };
258
259    template <class TR1, class TR2>
260    struct apply
261    {
262        struct get_second_types
263        {
264            template <class T1>
265            struct apply
266            {
267                typedef typename mpl::transform<
268                    TR2,
269                    bind_wrap1<
270                        mpl::bind2<make_pair, T1, mpl::_1>
271                    > >::type type;
272            };
273        };
274
275        typedef typename mpl::transform<
276            TR1,
277            get_second_types,
278            mpl::back_inserter<mpl::vector<> >
279            >::type pair_combinations; // nested sequence (vector of vectors) of
280                                       // pair combinations
281
282        // joins two sequences
283        struct join
284        {
285            template <class Seq1, class Seq2>
286            struct apply
287            {
288                typedef typename boost::mpl::copy<
289                    Seq2,
290                    boost::mpl::back_inserter<Seq1>
291                    >::type type;
292            };
293        };
294
295        // flattens a nested sequence
296        template<class Sequence>
297        struct flatten
298        {
299            typedef typename boost::mpl::fold<
300                Sequence,
301                typename boost::mpl::clear<Sequence>::type,
302                join
303                >::type type;
304        };
305
306        // the complete list of combinations
307        typedef typename flatten<pair_combinations>::type type;
308    };
309};
310
311// metafunction class to get the correct property map type
312template <class Scalar, class IndexMap>
313struct get_property_map_type
314{
315    typedef typename property_map_type::apply<Scalar, IndexMap>
316        ::type::unchecked_t type;
317};
318
319template <class IndexMap>
320struct get_property_map_type<keep_all, IndexMap>
321{
322    typedef keep_all type;
323};
324
325// this metafunction returns a filtered graph type, given the scalar types to be
326// used in the property maps
327struct get_graph_filtered
328{
329    template <class TypePair>
330    struct apply
331    {
332        typedef typename TypePair::first edge_scalar;
333        typedef typename TypePair::second vertex_scalar;
334
335        // if the 'scalar' is the index map itself, use simply that, otherwise
336        // get the specific property map
337        typedef typename mpl::if_<
338            is_same<edge_scalar,
339                    GraphInterface::edge_index_map_t>,
340            GraphInterface::edge_index_map_t,
341            typename get_property_map_type<
342                edge_scalar,
343                GraphInterface::edge_index_map_t>::type
344            >::type edge_property_map;
345
346        typedef typename mpl::if_<
347            is_same<vertex_scalar,
348                    GraphInterface::vertex_index_map_t>,
349            GraphInterface::vertex_index_map_t,
350            typename get_property_map_type<
351                vertex_scalar,
352                GraphInterface::vertex_index_map_t>::type
353            >::type vertex_property_map;
354
355        typedef typename graph_filter::apply<GraphInterface::multigraph_t,
356                                             edge_property_map,
357                                             vertex_property_map>::type type;
358    };
359};
360
361// this metafunction returns all the possible graph views
362struct get_all_graph_views
363{
364    template <class TypePairs,
365              class AlwaysDirected = mpl::bool_<false>,
366              class NeverDirected = mpl::bool_<false>,
367              class AlwaysReversed = mpl::bool_<false>,
368              class NeverReversed = mpl::bool_<false>,
369              class NeverFiltered = mpl::bool_<false> >
370    struct apply
371    {
372        // filtered graphs
373        struct filtered_graphs:
374            mpl::if_
375            <NeverFiltered,
376             mpl::vector<GraphInterface::multigraph_t>,
377             typename mpl::transform<TypePairs,
378                                     get_graph_filtered>::type>::type {};
379
380        // filtered + reversed graphs
381        struct reversed_graphs:
382            mpl::if_<AlwaysReversed,
383                     typename mpl::transform<filtered_graphs,
384                                             graph_reverse>::type,
385                     typename mpl::if_<
386                         NeverReversed,
387                         filtered_graphs,
388                         typename mpl::transform<
389                             filtered_graphs,
390                             graph_reverse,
391                             mpl::back_inserter<filtered_graphs>
392                             >::type
393                         >::type
394                >::type {};
395
396        // undirected + filtereed + reversed graphs
397        struct undirected_graphs:
398            mpl::if_<AlwaysDirected,
399                     reversed_graphs,
400                     typename mpl::if_<
401                         NeverDirected,
402                         typename mpl::transform<filtered_graphs,
403                                                 graph_undirect>::type,
404                         typename mpl::transform<
405                             filtered_graphs,
406                             graph_undirect,
407                             mpl::back_inserter<reversed_graphs>
408                             >::type
409                         >::type
410                     >::type {};
411        typedef undirected_graphs type;
412    };
413};
414
415// useful metafunction to split sequences in half
416struct split
417{
418    template <class Sequence>
419    struct get_element
420    {
421        template <class Index>
422        struct apply
423        {
424            typedef typename mpl::at<Sequence,Index>::type type;
425        };
426    };
427
428    template <class Sequence>
429    struct apply
430    {
431        typedef typename mpl::size<Sequence>::type size;
432        typedef typename mpl::divides<size, mpl::int_<2> >::type half_size;
433        typedef typename mpl::transform<mpl::range_c<int, 0, half_size::value>,
434                                        get_element<Sequence>,
435                                        mpl::back_inserter<mpl::vector<> > >
436            ::type first_part;
437        typedef typename mpl::transform<mpl::range_c<int, half_size::value,
438                                                     size::value>,
439                                        get_element<Sequence>,
440                                        mpl::back_inserter<mpl::vector<> > >
441            ::type second_part;
442        typedef typename mpl::pair<first_part,second_part> type;
443    };
444};
445
446
447
448// all scalar types plus edge and vertex index property (we actually only use
449// bool)
450
451#ifndef NO_GRAPH_FILTERING
452struct edge_scalars:
453    mpl::vector<keep_all, uint8_t> {};
454struct vertex_scalars:
455    mpl::vector<keep_all, uint8_t> {};
456#else
457struct edge_scalars:
458    mpl::vector<keep_all> {};
459struct vertex_scalars:
460    mpl::vector<keep_all> {};
461#endif
462
463// all scalar pairs
464struct scalar_pairs: get_all_pairs::apply<edge_scalars,vertex_scalars>::type {};
465
466// finally, this type should hold all the possible graph views
467struct all_graph_views:
468    get_all_graph_views::apply<scalar_pairs>::type {};
469
470// restricted graph views
471struct always_directed:
472    get_all_graph_views::apply<scalar_pairs,mpl::bool_<true> >::type {};
473
474struct never_directed:
475    get_all_graph_views::apply<scalar_pairs,mpl::bool_<false>,
476                               mpl::bool_<true> >::type {};
477
478struct always_reversed:
479    get_all_graph_views::apply<scalar_pairs,mpl::bool_<true>,
480                               mpl::bool_<false>,mpl::bool_<true> >::type {};
481
482struct never_reversed:
483    get_all_graph_views::apply<scalar_pairs,mpl::bool_<false>,
484                               mpl::bool_<false>,mpl::bool_<false>,
485                               mpl::bool_<true> >::type {};
486
487struct always_directed_never_reversed:
488    get_all_graph_views::apply<scalar_pairs,mpl::bool_<true>,
489                               mpl::bool_<false>,mpl::bool_<false>,
490                               mpl::bool_<true> >::type {};
491
492struct never_filtered:
493    get_all_graph_views::apply<scalar_pairs,mpl::bool_<false>,
494                               mpl::bool_<false>,mpl::bool_<false>,
495                               mpl::bool_<false>,mpl::bool_<true> >::type {};
496
497// sanity check
498typedef mpl::size<all_graph_views>::type n_views;
499#ifndef NO_GRAPH_FILTERING
500BOOST_MPL_ASSERT_RELATION(n_views::value, == , mpl::int_<12>::value);
501#else
502BOOST_MPL_ASSERT_RELATION(n_views::value, == , mpl::int_<3>::value);
503#endif
504
505// run_action() implementation
506// ===========================
507
508// wrap action to be called, to deal with property maps, i.e., return version
509// with no bounds checking.
510template <class Action, class Wrap>
511struct action_wrap
512{
513    action_wrap(Action a, GraphInterface& g, size_t max_v, size_t max_e)
514        : _a(a), _g(g), _max_v(max_v), _max_e(max_e) {}
515
516    template <class Type>
517    checked_vector_property_map<Type,GraphInterface::vertex_index_map_t>
518    uncheck(checked_vector_property_map
519            <Type,GraphInterface::vertex_index_map_t> a, mpl::true_) const
520    {
521        return a;
522    }
523
524    template <class Type>
525    unchecked_vector_property_map<Type,GraphInterface::vertex_index_map_t>
526    uncheck(checked_vector_property_map
527            <Type,GraphInterface::vertex_index_map_t> a, mpl::false_) const
528    {
529        return a.get_unchecked(_max_v);
530    }
531
532    template <class Type>
533    checked_vector_property_map<Type,GraphInterface::edge_index_map_t>
534    uncheck(checked_vector_property_map
535            <Type,GraphInterface::edge_index_map_t> a, mpl::true_) const
536    {
537        return a;
538    }
539
540    template <class Type>
541    unchecked_vector_property_map<Type,GraphInterface::edge_index_map_t>
542    uncheck(checked_vector_property_map
543            <Type,GraphInterface::edge_index_map_t> a, mpl::false_) const
544    {
545        return a.get_unchecked(_max_e);
546    }
547
548    template <class Type>
549    scalarS<typename Type::unchecked_t>
550    uncheck(scalarS<Type> a, mpl::false_) const
551    {
552        return scalarS<typename Type::unchecked_t>(uncheck(a._pmap,
553                                                           mpl::false_()));
554    }
555
556    //no op
557    template <class Type, class DoWrap>
558    Type uncheck(Type a, DoWrap) const { return a; }
559
560    template <class Graph>
561    GraphWrap<Graph> wrap(Graph* g, mpl::true_) const
562    {
563        return graph_wrap(*g, _g);
564    }
565
566    template <class Graph>
567    Graph& wrap(Graph* g, mpl::false_) const
568    {
569        return *g;
570    }
571
572    void operator()() const {};
573    template <class T1> void operator()(const T1& a1) const
574    { _a(wrap(a1, Wrap())); }
575    template <class T1, class T2>
576    void operator()(const T1& a1, const T2& a2) const
577    { _a(wrap(a1,Wrap()), uncheck(a2, Wrap())); }
578    template <class T1, class T2, class T3>
579    void operator()(const T1& a1, const T2& a2, const T3& a3) const
580    { _a(wrap(a1,Wrap()), uncheck(a2, Wrap()), uncheck(a3, Wrap()));}
581    template <class T1, class T2, class T3, class T4>
582    void operator()(const T1& a1, const T2& a2, const T3& a3, const T4& a4)
583        const
584    { _a(wrap(a1,Wrap()), uncheck(a2, Wrap()), uncheck(a3, Wrap()),
585         uncheck(a4, Wrap())); }
586    template <class T1, class T2, class T3, class T4, class T5>
587    void operator()(const T1& a1, const T2& a2, const T3& a3, const T4& a4,
588                    const T5& a5) const
589    { _a(wrap(a1,Wrap()), uncheck(a2, Wrap()), uncheck(a3, Wrap()),
590         uncheck(a4, Wrap()), uncheck(a5, Wrap())); }
591
592    Action _a;
593    reference_wrapper<GraphInterface> _g;
594    size_t _max_v, _max_e;
595};
596
597// this functor encapsulates another functor Action, which takes a pointer to a
598// graph view as first argument
599template <class Action, class GraphViews, class Wrap, class TR1, class TR2,
600          class TR3, class TR4 >
601struct graph_action
602{
603    struct graph_view_pointers:
604        mpl::transform<GraphViews, mpl::quote1<add_pointer> >::type {};
605
606    graph_action(GraphInterface& g, Action a)
607        : _g(g), _a(a, g, num_vertices(g._state->_mg),
608                    g._state->_max_edge_index + 1) {}
609
610    void operator()() const
611    {
612        bool found = false;
613        boost::any gview = _g.GetGraphView();
614        boost::mpl::for_each<graph_view_pointers>
615            (boost::mpl::select_types(_a, found, gview));
616        if (!found)
617        {
618            throw ActionNotFound(gview, typeid(Action),
619                                 vector<const std::type_info*>());
620        }
621    }
622
623    void operator()(boost::any a1) const
624    {
625        bool found = false;
626        boost::any gview = _g.GetGraphView();
627        boost::mpl::nested_for_each<graph_view_pointers,TR1>()
628            (boost::mpl::select_types(_a, found, gview, a1));
629        if (!found)
630        {
631            vector<const std::type_info*> args;
632            args.push_back(&a1.type());
633            throw ActionNotFound(gview, typeid(Action), args);
634        }
635    }
636
637    void operator()(boost::any a1, boost::any a2) const
638    {
639        bool found = false;
640        boost::any gview = _g.GetGraphView();
641        boost::mpl::nested_for_each<graph_view_pointers,TR1,TR2>()
642            (boost::mpl::select_types(_a, found, gview, a1, a2));
643        if (!found)
644        {
645            vector<const std::type_info*> args;
646            args.push_back(&a1.type());
647            args.push_back(&a2.type());
648            throw ActionNotFound(gview, typeid(Action), args);
649        }
650    }
651
652    void operator()(boost::any a1, boost::any a2, boost::any a3) const
653    {
654        bool found = false;
655        boost::any gview = _g.GetGraphView();
656        boost::mpl::nested_for_each<graph_view_pointers,TR1,TR2,TR3>()
657            (boost::mpl::select_types(_a, found, gview, a1, a2, a3));
658        if (!found)
659        {
660            vector<const std::type_info*> args;
661            args.push_back(&a1.type());
662            args.push_back(&a2.type());
663            args.push_back(&a3.type());
664            throw ActionNotFound(gview, typeid(Action), args);
665        }
666    }
667
668    void operator()(boost::any a1, boost::any a2, boost::any a3,
669                    boost::any a4) const
670    {
671        bool found = false;
672        boost::any gview = _g.GetGraphView();
673        boost::mpl::nested_for_each<graph_view_pointers,TR1,TR2,TR3,TR4>()
674            (boost::mpl::select_types(_a, found, gview, a1, a2, a3,a4));
675        if (!found)
676        {
677            vector<const std::type_info*> args;
678            args.push_back(&a1.type());
679            args.push_back(&a2.type());
680            args.push_back(&a3.type());
681            args.push_back(&a4.type());
682            throw ActionNotFound(gview, typeid(Action), args);
683        }
684    }
685
686    const GraphInterface &_g;
687    action_wrap<Action, Wrap> _a;
688};
689} // details namespace
690
691
692// all definitions of run_action with different arity
693template <class GraphViews = detail::all_graph_views, class Wrap = mpl::false_>
694struct run_action
695{
696    template <class Action>
697    detail::graph_action<Action,GraphViews,Wrap>
698    operator()(GraphInterface &g, Action a)
699    {
700        return detail::graph_action<Action,GraphViews,Wrap>(g, a);
701    }
702
703    template <class Action, class TR1>
704    detail::graph_action<Action,GraphViews,Wrap,TR1>
705    operator()(GraphInterface &g, Action a, TR1)
706    {
707        return detail::graph_action<Action,GraphViews,Wrap,TR1>(g, a);
708    }
709
710    template <class Action, class TR1, class TR2>
711    detail::graph_action<Action,GraphViews,Wrap,TR1,TR2>
712    operator()(GraphInterface &g, Action a, TR1, TR2)
713    {
714        return detail::graph_action<Action,GraphViews,Wrap,TR1,TR2>(g, a);
715    }
716
717    template <class Action, class TR1, class TR2, class TR3>
718    detail::graph_action<Action,GraphViews,Wrap,TR1,TR2,TR3>
719    operator()(GraphInterface &g, Action a, TR1, TR2, TR3)
720    {
721        return detail::graph_action<Action,GraphViews,Wrap,TR1,TR2,TR3>(g, a);
722    }
723
724    template <class Action, class TR1, class TR2, class TR3, class TR4>
725    detail::graph_action<Action,GraphViews,Wrap,TR1,TR2,TR3,TR4>
726    operator()(GraphInterface &g, Action a, TR1, TR2, TR3, TR4)
727    {
728        return detail::graph_action<Action,GraphViews,Wrap,TR1,TR2,TR3,TR4>(g, a);
729    }
730};
731
732// returns true if graph filtering was enabled at compile time
733bool graph_filtering_enabled();
734
735} //graph_tool namespace
736
737#endif // FILTERING_HH
Note: See TracBrowser for help on using the repository browser.