graph_python_interface.cc 32.7 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-2019 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));

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
} // namespace graph_tool

810 811
// register everything

812 813
void export_python_properties();

814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
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;
}

831
void export_python_interface()
832
{
833 834
    using namespace boost::python;

835
    class_<VertexBase>("VertexBase", no_init);
Tiago Peixoto's avatar
Tiago Peixoto committed
836
    class_<EdgeBase, boost::noncopyable>("EdgeBase", no_init);
837

838
    typedef boost::mpl::transform<graph_tool::all_graph_views,
839
                                  boost::mpl::quote1<std::add_const> >::type const_graph_views;
840
    typedef boost::mpl::transform<graph_tool::all_graph_views,
841 842 843 844
                                  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
845
    boost::mpl::for_each<graph_views>(std::bind(graph_tool::export_python_interface(),
846
                                                std::placeholders::_1, get_vlist(),
847
                                                get_elist(), graph_views()));
848
    export_python_properties();
849 850 851 852 853 854
    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> >);

855 856 857 858 859 860
    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);
861
    def("remove_vertex_array", graph_tool::remove_vertex_array);
862
    def("clear_vertex", graph_tool::clear_vertex);
863
    def("remove_edge", graph_tool::remove_edge);
864
    def("add_edge_list", graph_tool::do_add_edge_list);
865
    def("add_edge_list_hashed", graph_tool::do_add_edge_list_hashed);
866
    def("add_edge_list_iter", graph_tool::do_add_edge_list_iter);
867
    def("get_edge", get_edge);
868

869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
    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>);
885 886
    def("get_degree_list", get_degree_list);

887
    def("get_vertex_index", get_vertex_index);
888
    def("get_edge_index", do_get_edge_index);
889 890 891

    def("get_vlist", get_vlist);
    def("get_elist", get_elist);
892 893 894 895 896 897 898

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