graph_python_interface.cc 32.8 KB
Newer Older
1
2
// graph-tool -- a general graph modification and manipulation thingy
//
Tiago Peixoto's avatar
Tiago Peixoto committed
3
// Copyright (C) 2006-2020 Tiago de Paula Peixoto <tiago@skewed.de>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//
// 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 <http://www.gnu.org/licenses/>.

18
19
#include "graph_filtering.hh"
#include "graph.hh"
20
#include "graph_util.hh"
21
22
#include "graph_python_interface.hh"

23
#include <boost/python.hpp>
24
#include <boost/python/stl_iterator.hpp>
25
#include <set>
26

27
#include "coroutine.hh"
28

29
30
31
32
using namespace std;
using namespace boost;
using namespace graph_tool;

33
34
35
namespace graph_tool
{

36
37
38
struct get_vertex_iterator
{
    template <class Graph>
39
    void operator()(Graph& g, GraphInterface& gi,
40
41
                    python::object& iter) const
    {
42
        auto gp = retrieve_graph_view<Graph>(gi, g);
43
        typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
44
45
        iter = python::object(PythonIterator<Graph, PythonVertex<Graph>,
                                             vertex_iterator>(gp, vertices(g)));
46
47
48
    }
};

49
python::object get_vertices(GraphInterface& gi)
50
51
{
    python::object iter;
52
    run_action<>()(gi, std::bind(get_vertex_iterator(),
53
                                 std::placeholders::_1,
54
                                 std::ref(gi),
Tiago Peixoto's avatar
Tiago Peixoto committed
55
                                 std::ref(iter)))();
56
57
58
    return iter;
}

59
60
61
struct get_vertex_soft
{
    template <class Graph>
62
    void operator()(Graph& g, GraphInterface& gi, size_t i, python::object& v) const
63
    {
64
        auto gp = retrieve_graph_view<Graph>(gi, g);
65
66
67
68
69
        if (i < num_vertices(g))
            v = python::object(PythonVertex<Graph>(gp, vertex(i, g)));
        else
            v = python::object(PythonVertex<Graph>(gp,
                                                   graph_traits<Graph>::null_vertex()));
70
71
72
73
74
75
    }
};

struct get_vertex_hard
{
    template <class Graph>
76
    void operator()(Graph& g, GraphInterface& gi, size_t i, python::object& v) const
77
    {
78
        auto gp = retrieve_graph_view<Graph>(gi, g);
79
        size_t c = 0;
80
        for (auto vi : vertices_range(g))
81
82
        {
            if (c == i)
83
            {
84
                v = python::object(PythonVertex<Graph>(gp, vi));
85
86
                return;
            }
87
88
            ++c;
        }
89
90
        v = python::object(PythonVertex<Graph>(gp,
                                               graph_traits<Graph>::null_vertex()));
91
92
93
    }
};

94
python::object get_vertex(GraphInterface& gi, size_t i, bool use_index)
95
96
{
    python::object v;
97
    if (!use_index)
98
        run_action<>()(gi,
99
                       std::bind(get_vertex_hard(), std::placeholders::_1,
100
                                 std::ref(gi), i, std::ref(v)))();
101
    else
102
        run_action<>()(gi,
103
                       std::bind(get_vertex_soft(), std::placeholders::_1,
104
                                 std::ref(gi), i, std::ref(v)))();
105
106
107
    return v;
}

108
109
110
struct get_edge_iterator
{
    template <class Graph>
111
    void operator()(Graph& g, GraphInterface& gi, python::object& iter)
112
        const
113
    {
114
        auto gp = retrieve_graph_view<Graph>(gi, g);
115
        typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
116
117
        iter = python::object(PythonIterator<Graph, PythonEdge<Graph>,
                                             edge_iterator>(gp, edges(g)));
118
119
120
    }
};

121
python::object get_edges(GraphInterface& gi)
122
123
{
    python::object iter;
124
    run_action<>()(gi, std::bind(get_edge_iterator(), std::placeholders::_1,
125
                                 std::ref(gi), std::ref(iter)))();
126
127
128
    return iter;
}

129
struct add_new_vertex
130
{
131
132
133
    template <class Graph>
    void operator()(Graph& g, GraphInterface& gi, size_t n,
                    python::object& new_v) const
134
    {
135
        auto gp = retrieve_graph_view<Graph>(gi, g);
136
        if (n != 1)
137
138
139
140
141
142
143
144
145
        {
            for (size_t i = 0; i < n; ++i)
                add_vertex(g);
            new_v = python::object();
        }
        else
        {
            new_v = python::object(PythonVertex<Graph>(gp, add_vertex(g)));
        }
146
    }
147
148
149
150
151
152
};


python::object add_vertex(GraphInterface& gi, size_t n)
{
    python::object v;
153
    run_action<>()(gi, std::bind(add_new_vertex(), std::placeholders::_1,
154
155
                                 std::ref(gi), n, std::ref(v)))();
    return v;
156
157
158
}


159
void remove_vertex_array(GraphInterface& gi, const python::object& oindex, bool fast)
160
{
161
    boost::multi_array_ref<int64_t,1> index = get_array<int64_t,1>(oindex);
162
    auto& g = gi.get_graph();
163
    if (fast)
164
165
166
167
    {
        for (auto v : index)
            remove_vertex_fast(vertex(v, g), g);
    }
168
    else
169
170
171
172
    {
        for (auto v : index)
            remove_vertex(vertex(v, g), g);
    }
173
174
}

175
176
void remove_vertex(GraphInterface& gi, size_t v, bool fast)
{
177
    auto& g = gi.get_graph();
178
179
180
181
182
183
184
185
186
187
    if (fast)
    {
        remove_vertex_fast(vertex(v, g), g);
    }
    else
    {
        remove_vertex(vertex(v, g), g);
    }
}

188
189
190
191
192
193
194
195
196
197
198
struct do_clear_vertex
{
    template <class Graph>
    void operator()(Graph& g, size_t v) const
    {
        clear_vertex(vertex(v, g), g);
    }
};

void clear_vertex(GraphInterface& gi, size_t v)
{
199
    run_action<>()(gi, std::bind(do_clear_vertex(), std::placeholders::_1, v))();
200
}
201

202
203
struct add_new_edge
{
204
205
206
    template <class Graph>
    void operator()(Graph& g, GraphInterface& gi, size_t s, size_t t,
                    python::object& new_e) const
207
    {
208
        auto gp = retrieve_graph_view<Graph>(gi, g);
209
210
        auto e = add_edge(vertex(s, g), vertex(t, g), g).first;
        new_e = python::object(PythonEdge<Graph>(gp, e));
211
212
213
    }
};

214
python::object add_edge(GraphInterface& gi, size_t s, size_t t)
215
216
{
    python::object new_e;
217
    run_action<>()(gi, std::bind(add_new_edge(), std::placeholders::_1, std::ref(gi),
218
                                 s, t, std::ref(new_e)))();
219
220
221
    return new_e;
}

Tiago Peixoto's avatar
Tiago Peixoto committed
222
void remove_edge(GraphInterface& gi, EdgeBase& e)
223
{
Tiago Peixoto's avatar
Tiago Peixoto committed
224
225
226
227
    e.check_valid();
    auto edge = e.get_descriptor();
    run_action<>()(gi, [&](auto& g) { remove_edge(edge, g); })();
    e.invalidate();
228
229
}

230
231
232
struct get_edge_dispatch
{
    template <class Graph>
233
    void operator()(Graph& g, GraphInterface& gi, size_t s, size_t t,
234
235
                    bool all_edges, boost::python::list& es) const
    {
236
        auto gp = retrieve_graph_view<Graph>(gi, g);
237
        size_t k_t = graph_tool::is_directed(g) ?
238
239
            in_degreeS()(t, g) : out_degree(t, g);
        if (out_degree(s, g) <= k_t)
240
        {
241
            for (auto e : out_edges_range(vertex(s, g), g))
242
            {
243
244
245
246
247
248
249
250
251
252
253
254
                if (target(e, g) == vertex(t, g))
                {
                    es.append(PythonEdge<Graph>(gp, e));
                    if (!all_edges)
                        break;
                }
            }
        }
        else
        {
            for (auto e : in_or_out_edges_range(vertex(t, g), g))
            {
255
                auto w = source(e, g);
256
257
                if (w == vertex(s, g))
                {
258
                    if (!graph_tool::is_directed(g) && e.s != s)
259
                        std::swap(e.s, e.t);
260
261
262
263
                    es.append(PythonEdge<Graph>(gp, e));
                    if (!all_edges)
                        break;
                }
264
265
266
267
268
            }
        }
    }
};

269
python::object get_edge(GraphInterface& gi, size_t s, size_t t, bool all_edges)
270
271
{
    python::list es;
272
    run_action<>()(gi, std::bind(get_edge_dispatch(), std::placeholders::_1,
273
274
                                 std::ref(gi), s, t, all_edges,
                                 std::ref(es)))();
275
276
277
278
    return es;
}


279
280
struct get_degree_map
{
281
282
    template <class Graph, class DegS, class Weight>
    void operator()(const Graph& g, python::object& odeg_map, DegS deg, Weight weight) const
283
    {
284
        typedef typename detail::get_weight_type<Weight>::type weight_t;
Tiago Peixoto's avatar
Tiago Peixoto committed
285
        typedef typename mpl::if_<std::is_same<weight_t, size_t>, int32_t, weight_t>::type deg_t;
286

287
        typedef typename vprop_map_t<deg_t>::type map_t;
288
289
290
291

        map_t cdeg_map(get(vertex_index, g));
        typename map_t::unchecked_t deg_map = cdeg_map.get_unchecked(num_vertices(g));

Tiago Peixoto's avatar
Tiago Peixoto committed
292
293
294
295
296
297
        parallel_vertex_loop
            (g,
             [&](auto v)
             {
                 deg_map[v] = deg(v, g, weight);
             });
298
299

        odeg_map = python::object(PythonPropertyMap<map_t>(cdeg_map));
300
301
302
    }
};

303
python::object GraphInterface::degree_map(string deg, boost::any weight) const
304
305
{

306
307
    python::object deg_map;

308
309
    typedef mpl::push_back<edge_scalar_properties,
                           detail::no_weightS>::type weight_t;
310
311
    if (weight.empty())
        weight = detail::no_weightS();
312
313

    if (deg == "in")
314
        run_action<>()(const_cast<GraphInterface&>(*this),
315
316
                       std::bind(get_degree_map(), std::placeholders::_1,
                                 std::ref(deg_map), in_degreeS(), std::placeholders::_2), weight_t())
317
            (weight);
318
    else if (deg == "out")
319
        run_action<>()(const_cast<GraphInterface&>(*this),
320
321
                       std::bind(get_degree_map(), std::placeholders::_1,
                                 std::ref(deg_map), out_degreeS(), std::placeholders::_2), weight_t())
322
            (weight);
323
    else if (deg == "total")
324
        run_action<>()(const_cast<GraphInterface&>(*this),
325
326
                       std::bind(get_degree_map(), std::placeholders::_1,
                                 std::ref(deg_map), total_degreeS(), std::placeholders::_2), weight_t())
327
328
            (weight);
    return deg_map;
329
330
}

331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
template <class PMaps>
int value_type_promotion(std::vector<boost::any>& props)
{
    int type_pos = boost::mpl::find<value_types,int64_t>::type::pos::value;
    for (auto& aep : props)
    {
        gt_dispatch<>()([&](auto& ep)
                        {
                            typedef std::remove_reference_t<decltype(ep)> pmap_t;
                            typedef typename property_traits<pmap_t>::value_type val_t;
                            if (std::is_same<val_t, size_t>::value)
                                return;
                            constexpr int ep_t = boost::mpl::find<value_types,val_t>::type::pos::value;
                            type_pos = std::max(type_pos, ep_t);
                        },
                        PMaps())(aep);
    }
    return type_pos;
349
350
}

351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
template <int kind>
python::object get_vertex_list(GraphInterface& gi, size_t v,
                               python::list ovprops)
{
    std::vector<boost::any> avprops;
    for (int i = 0; i < python::len(ovprops); ++i)
    {
        avprops.push_back(python::extract<boost::any>(ovprops[i])());
        if (!belongs<vertex_scalar_properties>()(avprops.back()))
            throw ValueException("vertex property map must be of scalar type");
    }

    int vtype = boost::mpl::find<value_types,int64_t>::type::pos::value;
    if (!avprops.empty())
        vtype = value_type_promotion<vertex_scalar_properties>(avprops);

    python::object ret;
    auto dispatch =
        [&](auto&& vrange)
        {
            mpl::for_each<scalar_types>(
                [&](auto t)
                {
                    typedef decltype(t) t_t;
                    if (vtype != boost::mpl::find<value_types, t_t>::type::pos::value)
                        return;
                    typedef DynamicPropertyMapWrap<t_t, GraphInterface::vertex_t>
                        converted_map_t;
                    std::vector<converted_map_t> vprops;
                    for (auto& aep: avprops)
                        vprops.emplace_back(aep, vertex_scalar_properties());

                    std::vector<t_t> vlist;
                    run_action<>()(gi,
                                   [&](auto& g)
                                   {
                                       for (auto u: vrange(g))
                                       {
                                           vlist.push_back(u);
                                           for (auto& vp : vprops)
                                               vlist.push_back(get(vp, u));
                                       }
                                   })();
                    ret = wrap_vector_owned(vlist);
                });
        };
397

398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
    switch (kind)
    {
    case 0:
        dispatch([&](auto& g){return vertices_range(g);});
        break;
    case 1:
        dispatch([&](auto& g){return out_neighbors_range(v, g);});
        break;
    case 2:
        dispatch([&](auto& g){return in_neighbors_range(v, g);});
        break;
    case 3:
        dispatch([&](auto& g){return all_neighbors_range(v, g);});
    }
    return ret;
413
414
}

415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
enum range_t { FULL, OUT, IN, ALL };

template <int kind>
python::object get_vertex_iter(GraphInterface& gi, int v, python::list ovprops)
{
#ifdef HAVE_BOOST_COROUTINE
    auto dispatch = [&](auto&&vrange)
        {
            auto yield_dispatch = [&](auto& yield)
                {
                    if (python::len(ovprops) == 0)
                    {
                        run_action<>()(gi,
                                       [&](auto& g)
                                       {
                                           for (auto v: vertices_range(g))
                                               yield(python::object(v));
                                       })();
                    }
                    else
                    {
                        typedef DynamicPropertyMapWrap<python::object,
                                                       GraphInterface::vertex_t>
                            converted_map_t;

                        std::vector<converted_map_t> vprops;
                        for (int i = 0; i < python::len(ovprops); ++i)
                            vprops.emplace_back(python::extract<boost::any>(ovprops[i])(),
                                                vertex_properties());
                        run_action<>()(gi,
                                       [&](auto& g)
                                       {
                                           for (auto v: vrange(g))
                                           {
                                               python::list vlist;
                                               vlist.append(python::object(v));
                                               for (auto& vp : vprops)
                                                   vlist.append(get(vp, v));
                                               yield(vlist);
                                           }
                                       })();
                    }
                };
            return boost::python::object(CoroGenerator(yield_dispatch));
        };
    switch (kind)
    {
    case range_t::FULL:
        return dispatch([&](auto& g){return vertices_range(g);});
    case range_t::OUT:
        return dispatch([&](auto& g){return out_neighbors_range(v, g);});
    case range_t::IN:
        return dispatch([&](auto& g){return in_neighbors_range(v, g);});
    case range_t::ALL:
        return dispatch([&](auto& g){return all_neighbors_range(v, g);});
    }
#else
    throw GraphException("This functionality is not available because boost::coroutine was not found at compile-time");
#endif
474
475
}

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
template <int kind>
python::object get_edge_list(GraphInterface& gi, size_t v, python::list oeprops)
{
    std::vector<boost::any> aeprops;
    for (int i = 0; i < python::len(oeprops); ++i)
    {
        aeprops.push_back(python::extract<boost::any>(oeprops[i])());
        if (!belongs<edge_scalar_properties>()(aeprops.back()))
            throw ValueException("edge property map must be of scalar type");
    }

    int etype = boost::mpl::find<value_types,int64_t>::type::pos::value;
    if (!aeprops.empty())
        etype = value_type_promotion<edge_scalar_properties>(aeprops);

    python::object ret;
    auto dispatch =
        [&](auto&& erange)
        {
            mpl::for_each<scalar_types>(
                [&](auto t)
                {
                    typedef decltype(t) t_t;
                    if (etype != boost::mpl::find<value_types, t_t>::type::pos::value)
                        return;
                    typedef DynamicPropertyMapWrap<t_t, GraphInterface::edge_t>
                        converted_map_t;
                    std::vector<converted_map_t> eprops;
                    for (auto& aep: aeprops)
                        eprops.emplace_back(aep, edge_scalar_properties());

                    std::vector<t_t> elist;
                    run_action<>()(gi,
                                   [&](auto& g)
                                   {
                                       for (auto e: erange(g))
                                       {
                                           elist.push_back(source(e, g));
                                           elist.push_back(target(e, g));
                                           for (auto& ep : eprops)
                                               elist.push_back(get(ep,e));
                                       }
                                   })();
                    ret = wrap_vector_owned(elist);
                });
        };
    switch (kind)
    {
    case range_t::FULL:
        dispatch([&](auto& g){return edges_range(g);});
        break;
    case range_t::OUT:
        dispatch([&](auto& g){return out_edges_range(v, g);});
        break;
    case range_t::IN:
        dispatch([&](auto& g){return in_edges_range(v, g);});
        break;
    case range_t::ALL:
        dispatch([&](auto& g){return all_edges_range(v, g);});
    }
    return ret;
537
538
}

539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
template <int kind>
python::object get_edge_iter(GraphInterface& gi, size_t v, python::list oeprops)
{
#ifdef HAVE_BOOST_COROUTINE
   auto dispatch = [&](auto&&erange)
        {
            auto yield_dispatch = [&](auto& yield)
                {
                    typedef DynamicPropertyMapWrap<python::object,
                                                   GraphInterface::edge_t>
                        converted_map_t;

                    std::vector<converted_map_t> eprops;
                    for (int i = 0; i < python::len(oeprops); ++i)
                        eprops.emplace_back(python::extract<boost::any>(oeprops[i])(),
                                            edge_properties());
                    run_action<>()(gi,
                                   [&](auto& g)
                                   {
                                       for (auto e: erange(g))
                                       {
                                           python::list elist;
                                           elist.append(python::object(source(e, g)));
                                           elist.append(python::object(target(e, g)));
                                           for (auto& ep : eprops)
                                               elist.append(get(ep, e));
                                           yield(elist);
                                       }
                                   })();
                };
            return boost::python::object(CoroGenerator(yield_dispatch));
        };
   switch (kind)
    {
    case range_t::FULL:
        return dispatch([&](auto& g){return edges_range(g);});
    case range_t::OUT:
        return dispatch([&](auto& g){return out_edges_range(v, g);});
    case range_t::IN:
        return dispatch([&](auto& g){return in_edges_range(v, g);});
    case range_t::ALL:
        return dispatch([&](auto& g){return all_edges_range(v, g);});
    }
#else
    throw GraphException("This functionality is not available because boost::coroutine was not found at compile-time");
#endif
585
586
587
}

python::object get_degree_list(GraphInterface& gi, python::object ovlist,
588
                               boost::any eprop, int kind)
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
{
    python::object ret;
    auto vlist = get_array<uint64_t,1>(ovlist);

    typedef UnityPropertyMap<size_t,
                             graph_traits<GraphInterface::multigraph_t>::edge_descriptor>
        empty_t;
    if (eprop.empty())
    {
        eprop = empty_t();
    }
    else
    {
        if (!belongs<edge_scalar_properties>()(eprop))
            throw ValueException("edge weight property map must be of scalar type");
    }

    typedef mpl::push_back<edge_scalar_properties,
                           empty_t>::type eprops_t;

    auto get_degs = [&](auto deg)
        {
            run_action<>()(gi,
                           [&](auto& g, auto& ew)
                           {
                               typedef typename std::remove_reference
                                   <decltype(ew)>::type::value_type val_t;
                               std::vector<val_t> dlist;
                               dlist.reserve(vlist.size());
                               for (auto v : vlist)
                               {
                                   if (!is_valid_vertex(v, g))
                                       throw ValueException("invalid vertex: " +
                                                            lexical_cast<string>(v));
                                   dlist.push_back(val_t(deg(v, g, ew)));
                               }
                               ret = wrap_vector_owned(dlist);
                           }, eprops_t())(eprop);
        };

629
630
631
    switch (kind)
    {
    case 0:
632
        get_degs(out_degreeS());
633
634
        break;
    case 1:
635
        get_degs(in_degreeS());
636
        break;
637
    case 2:
638
639
640
        get_degs(total_degreeS());
        break;
    }
641
642
643
    return ret;
}

644
645
646
647
648
//
// Below are the functions with will properly register all the types to python,
// for every filter, type, etc.
//

649
650
651
// this will register all the Vertex/Edge classes to python
struct export_python_interface
{
652
653
654
    template <class Graph, class GraphViews>
    void operator()(Graph* gp, python::list vclasses,
                    python::list eclasses, GraphViews) const
655
656
    {
        using namespace boost::python;
657

658
659
        class_<PythonVertex<Graph>, bases<VertexBase>> vclass("Vertex", no_init);
        vclass
660
            .def("__in_degree", &PythonVertex<Graph>::get_in_degree,
661
                 "Return the in-degree.")
662
            .def("__weighted_in_degree", &PythonVertex<Graph>::get_weighted_in_degree,
663
                 "Return the weighted in-degree.")
664
            .def("__out_degree", &PythonVertex<Graph>::get_out_degree,
665
                 "Return the out-degree.")
666
            .def("__weighted_out_degree", &PythonVertex<Graph>::get_weighted_out_degree,
667
                 "Return the weighted out-degree.")
668
            .def("in_edges", &PythonVertex<Graph>::in_edges,
669
                 "Return an iterator over the in-edges.")
670
            .def("out_edges", &PythonVertex<Graph>::out_edges,
671
                 "Return an iterator over the out-edges.")
672
            .def("is_valid", &PythonVertex<Graph>::is_valid,
673
                 "Return whether the vertex is valid.")
674
675
676
677
678
            .def("graph_ptr", &PythonVertex<Graph>::get_graph_ptr)
            .def("graph_type", &PythonVertex<Graph>::get_graph_type)
            .def("__str__", &PythonVertex<Graph>::get_string)
            .def("__int__", &PythonVertex<Graph>::get_index)
            .def("__hash__", &PythonVertex<Graph>::get_hash);
679
680
681
682
683

        vclasses.append(vclass);

        class_<PythonEdge<Graph>, bases<EdgeBase>> eclass("Edge", no_init);
        eclass
684
            .def("source", &PythonEdge<Graph>::get_source,
685
                 "Return the source vertex.")
686
            .def("target", &PythonEdge<Graph>::get_target,
687
                 "Return the target vertex.")
688
            .def("is_valid", &PythonEdge<Graph>::is_valid,
689
                 "Return whether the edge is valid.")
690
691
            .def("graph_ptr", &PythonEdge<Graph>::get_graph_ptr)
            .def("graph_type", &PythonEdge<Graph>::get_graph_type)
692
693
            .def("__str__", &PythonEdge<Graph>::get_string)
            .def("__hash__", &PythonEdge<Graph>::get_hash);
694

695
696
697
698
699
700
        boost::mpl::for_each<GraphViews>(std::bind(export_python_interface(),
                                                   gp, std::placeholders::_1,
                                                   std::ref(eclass)));

        eclasses.append(eclass);

701
        typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
702
703
704
705
        class_<PythonIterator<Graph, PythonVertex<Graph>, vertex_iterator> >
            ("VertexIterator", no_init)
            .def("__iter__", objects::identity_function())
            .def("__next__", &PythonIterator<Graph, PythonVertex<Graph>,
706
                                             vertex_iterator>::next)
707
            .def("next", &PythonIterator<Graph, PythonVertex<Graph>,
708
                                         vertex_iterator>::next);
709

710
        typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
711
        class_<PythonIterator<Graph, PythonEdge<Graph>,
712
713
                              edge_iterator> >("EdgeIterator", no_init)
            .def("__iter__", objects::identity_function())
714
            .def("__next__", &PythonIterator<Graph, PythonEdge<Graph>,
715
                                             edge_iterator>::next)
716
            .def("next", &PythonIterator<Graph, PythonEdge<Graph>,
717
                                         edge_iterator>::next);
718
719
720

        typedef typename graph_traits<Graph>::out_edge_iterator
            out_edge_iterator;
721
        class_<PythonIterator<Graph, PythonEdge<Graph>,
722
723
                              out_edge_iterator> >("OutEdgeIterator", no_init)
            .def("__iter__", objects::identity_function())
724
            .def("__next__", &PythonIterator<Graph, PythonEdge<Graph>,
725
                                             out_edge_iterator>::next)
726
            .def("next", &PythonIterator<Graph, PythonEdge<Graph>,
727
                                         out_edge_iterator>::next);
728
729
730

        typedef typename graph_traits<Graph>::directed_category
            directed_category;
Tiago Peixoto's avatar
Tiago Peixoto committed
731
732
        typedef typename std::is_convertible<directed_category,
                                             boost::directed_tag>::type is_directed;
733
734
735
        if (is_directed::value)
        {
            typedef typename in_edge_iteratorS<Graph>::type in_edge_iterator;
736
            class_<PythonIterator<Graph, PythonEdge<Graph>,
737
738
                                  in_edge_iterator> >("InEdgeIterator", no_init)
                .def("__iter__", objects::identity_function())
739
                .def("__next__", &PythonIterator<Graph, PythonEdge<Graph>,
740
                                                 in_edge_iterator>::next)
741
                .def("next", &PythonIterator<Graph, PythonEdge<Graph>,
742
                                             in_edge_iterator>::next);
743
744
        }
    }
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781

    template <class Graph, class OGraph, class Eclass>
    void operator()(Graph*, OGraph*, Eclass& eclass) const
    {
        std::function<bool(const PythonEdge<Graph>&,
                           const PythonEdge<OGraph>&)> eq =
            [] (const PythonEdge<Graph>& e1,
                const PythonEdge<OGraph>& e2) -> bool { return e1 == e2; };
        std::function<bool(const PythonEdge<Graph>& e1,
                           const PythonEdge<OGraph>&)> ne =
            [] (const PythonEdge<Graph>& e1,
                const PythonEdge<OGraph>& e2) -> bool { return e1 != e2; };
        std::function<bool(const PythonEdge<Graph>&,
                           const PythonEdge<OGraph>&)> gt =
            [] (const PythonEdge<Graph>& e1,
                const PythonEdge<OGraph>& e2) -> bool { return e1 > e2; };
        std::function<bool(const PythonEdge<Graph>&,
                           const PythonEdge<OGraph>&)> lt =
            [] (const PythonEdge<Graph>& e1,
                const PythonEdge<OGraph>& e2) -> bool { return e1 < e2; };
        std::function<bool(const PythonEdge<Graph>&,
                           const PythonEdge<OGraph>&)> ge =
            [] (const PythonEdge<Graph>& e1,
                const PythonEdge<OGraph>& e2) -> bool { return e1 >= e2; };
        std::function<bool(const PythonEdge<Graph>&,
                           const PythonEdge<OGraph>&)> le =
            [] (const PythonEdge<Graph>& e1,
                const PythonEdge<OGraph>& e2) -> bool { return e1 <= e2; };

        eclass
            .def("__eq__", eq)
            .def("__ne__", ne)
            .def("__lt__", lt)
            .def("__gt__", gt)
            .def("__le__", le)
            .def("__ge__", ge);
    }
782
783
};

784
785
786
787
PythonPropertyMap<GraphInterface::vertex_index_map_t>
get_vertex_index(GraphInterface& g)
{
    return PythonPropertyMap<GraphInterface::vertex_index_map_t>
788
        (g.get_vertex_index());
789
790
791
}

PythonPropertyMap<GraphInterface::edge_index_map_t>
792
do_get_edge_index(GraphInterface& g)
793
794
{
    return PythonPropertyMap<GraphInterface::edge_index_map_t>
795
        (g.get_edge_index());
796
797
}

798
void do_add_edge_list(GraphInterface& gi, python::object aedge_list,
799
                      python::object eprops);
800
801

void do_add_edge_list_hashed(GraphInterface& gi, python::object aedge_list,
802
                             boost::any& vertex_map, bool is_str,
803
                             python::object eprops);
804
805

void do_add_edge_list_iter(GraphInterface& gi, python::object edge_list,
806
                           python::object eprops);
807

808
809
810
811
812
bool hasattr(boost::python::object obj, std::string const& attrName)
{
    return PyObject_HasAttrString(obj.ptr(), attrName.c_str());
}

813
814
} // namespace graph_tool

815
816
// register everything

817
818
void export_python_properties();

819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
python::list* _vlist(0);
python::list* _elist(0);

python::list get_vlist()
{
    if (_vlist == nullptr)
        _vlist = new python::list();
    return *_vlist;
}

python::list get_elist()
{
    if (_elist == nullptr)
        _elist = new python::list();
    return *_elist;
}

836
void export_python_interface()
837
{
838
839
    using namespace boost::python;

840
    class_<VertexBase>("VertexBase", no_init);
Tiago Peixoto's avatar
Tiago Peixoto committed
841
    class_<EdgeBase, boost::noncopyable>("EdgeBase", no_init);
842

843
    typedef boost::mpl::transform<graph_tool::all_graph_views,
844
                                  boost::mpl::quote1<std::add_const> >::type const_graph_views;
845
    typedef boost::mpl::transform<graph_tool::all_graph_views,
846
847
848
849
                                  boost::mpl::quote1<std::add_pointer> >::type all_graph_views;
    typedef boost::mpl::transform<const_graph_views,
                                  boost::mpl::quote1<std::add_pointer> >::type all_const_graph_views;
    typedef boost::mpl::joint_view<all_graph_views, all_const_graph_views>::type graph_views;
Tiago Peixoto's avatar
Tiago Peixoto committed
850
    boost::mpl::for_each<graph_views>(std::bind(graph_tool::export_python_interface(),
851
                                                std::placeholders::_1, get_vlist(),
852
                                                get_elist(), graph_views()));
853
    export_python_properties();
854
855
856
857
858
859
    def("new_vertex_property",
        &new_property<GraphInterface::vertex_index_map_t>);
    def("new_edge_property", &new_property<GraphInterface::edge_index_map_t>);
    def("new_graph_property",
        &new_property<ConstantPropertyMap<size_t,graph_property_tag> >);

860
861
862
863
864
865
    def("get_vertex", get_vertex);
    def("get_vertices", get_vertices);
    def("get_edges", get_edges);
    def("add_vertex", graph_tool::add_vertex);
    def("add_edge", graph_tool::add_edge);
    def("remove_vertex", graph_tool::remove_vertex);
866
    def("remove_vertex_array", graph_tool::remove_vertex_array);
867
    def("clear_vertex", graph_tool::clear_vertex);
868
    def("remove_edge", graph_tool::remove_edge);
869
    def("add_edge_list", graph_tool::do_add_edge_list);
870
    def("add_edge_list_hashed", graph_tool::do_add_edge_list_hashed);
871
    def("add_edge_list_iter", graph_tool::do_add_edge_list_iter);
872
    def("get_edge", get_edge);
873

874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
    def("get_vertex_list", &get_vertex_list<range_t::FULL>);
    def("get_vertex_iter", &get_vertex_iter<range_t::FULL>);
    def("get_edge_list", &get_edge_list<range_t::FULL>);
    def("get_edge_iter", &get_edge_iter<range_t::FULL>);
    def("get_out_edge_list", &get_edge_list<range_t::OUT>);
    def("get_out_edge_iter", &get_edge_iter<range_t::OUT>);
    def("get_in_edge_list", &get_edge_list<range_t::IN>);
    def("get_in_edge_iter", &get_edge_iter<range_t::IN>);
    def("get_all_edge_list", &get_edge_list<range_t::ALL>);
    def("get_all_edge_iter", &get_edge_iter<range_t::ALL>);
    def("get_out_neighbors_list", &get_vertex_list<range_t::OUT>);
    def("get_out_neighbors_iter", &get_vertex_iter<range_t::OUT>);
    def("get_in_neighbors_list", &get_vertex_list<range_t::IN>);
    def("get_in_neighbors_iter", &get_vertex_iter<range_t::IN>);
    def("get_all_neighbors_list", &get_vertex_list<range_t::ALL>);
    def("get_all_neighbors_iter", &get_vertex_iter<range_t::ALL>);
890
891
    def("get_degree_list", get_degree_list);

892
    def("get_vertex_index", get_vertex_index);
893
    def("get_edge_index", do_get_edge_index);
894
895
896

    def("get_vlist", get_vlist);
    def("get_elist", get_elist);
897
898
899
900
901
902
903

#ifdef HAVE_BOOST_COROUTINE
    class_<CoroGenerator>("CoroGenerator", no_init)
        .def("__iter__", objects::identity_function())
        .def("next", &CoroGenerator::next)
        .def("__next__", &CoroGenerator::next);
#endif
904
}