graph_python_interface.cc 34.6 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-2015 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
20
21
22
23
24
25
26
27
28
#include <boost/mpl/vector.hpp>
#include <functional>
namespace boost { namespace python { namespace detail {
    template <class R_, class P0_, class P1_, class T=void>
    boost::mpl::vector<R_, P0_, P1_>
    get_signature(std::function<R_ (P0_, P1_)>&, T* = nullptr)
    {
        return boost::mpl::vector<R_, P0_, P1_>();
    }
} } }

29
30
31
32
#include "graph_filtering.hh"
#include "graph.hh"
#include "graph_python_interface.hh"

33
#include <boost/python.hpp>
34
#include <boost/python/stl_iterator.hpp>
35
#include <set>
36

37

38
39
40
41
using namespace std;
using namespace boost;
using namespace graph_tool;

42
43
44
namespace graph_tool
{

45
46
47
struct get_vertex_iterator
{
    template <class Graph>
48
    void operator()(Graph& g, GraphInterface& gi,
49
50
                    python::object& iter) const
    {
51
        std::shared_ptr<Graph> gp = retrieve_graph_view<Graph>(gi, g);
52
        typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
53
54
        iter = python::object(PythonIterator<Graph, PythonVertex<Graph>,
                                             vertex_iterator>(gp, vertices(g)));
55
56
57
    }
};

58
python::object get_vertices(GraphInterface& gi)
59
60
{
    python::object iter;
61
62
63
    run_action<>()(gi, std::bind(get_vertex_iterator(),
                                 placeholders::_1,
                                 std::ref(gi),
Tiago Peixoto's avatar
Tiago Peixoto committed
64
                                 std::ref(iter)))();
65
66
67
    return iter;
}

68
69
70
struct get_vertex_soft
{
    template <class Graph>
71
    void operator()(Graph& g, GraphInterface& gi, size_t i, python::object& v) const
72
    {
73
74
        std::shared_ptr<Graph> gp = retrieve_graph_view<Graph>(gi, g);
        v = python::object(PythonVertex<Graph>(gp, vertex(i, g)));
75
76
77
78
79
80
    }
};

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

99
python::object get_vertex(GraphInterface& gi, size_t i)
100
101
{
    python::object v;
102
    if (gi.is_vertex_filter_active())
103
        run_action<>()(gi,
Tiago Peixoto's avatar
Tiago Peixoto committed
104
                       std::bind(get_vertex_hard(), placeholders::_1,
105
                                 std::ref(gi), i, std::ref(v)))();
106
    else
107
        run_action<>()(gi,
Tiago Peixoto's avatar
Tiago Peixoto committed
108
                       std::bind(get_vertex_soft(), placeholders::_1,
109
                                 std::ref(gi), i, std::ref(v)))();
110
111
112
    return v;
}

113
114
115
struct get_edge_iterator
{
    template <class Graph>
116
    void operator()(Graph& g, GraphInterface& gi, python::object& iter)
117
        const
118
    {
119
        std::shared_ptr<Graph> gp = retrieve_graph_view<Graph>(gi, g);
120
        typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
121
122
        iter = python::object(PythonIterator<Graph, PythonEdge<Graph>,
                                             edge_iterator>(gp, edges(g)));
123
124
125
    }
};

126
python::object get_edges(GraphInterface& gi)
127
128
{
    python::object iter;
129
130
    run_action<>()(gi, std::bind(get_edge_iterator(), placeholders::_1,
                                 std::ref(gi), std::ref(iter)))();
131
132
133
    return iter;
}

134
struct add_new_vertex
135
{
136
137
138
    template <class Graph>
    void operator()(Graph& g, GraphInterface& gi, size_t n,
                    python::object& new_v) const
139
    {
140
141
142
143
144
145
146
147
148
149
150
        std::shared_ptr<Graph> gp = retrieve_graph_view<Graph>(gi, g);
        if (n > 1)
        {
            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)));
        }
151
    }
152
153
154
155
156
157
158
159
160
};


python::object add_vertex(GraphInterface& gi, size_t n)
{
    python::object v;
    run_action<>()(gi, std::bind(add_new_vertex(), placeholders::_1,
                                 std::ref(gi), n, std::ref(v)))();
    return v;
161
162
163
}


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

180
181
void remove_vertex(GraphInterface& gi, size_t v, bool fast)
{
182
    auto& g = gi.get_graph();
183
184
185
186
187
188
189
190
191
192
193
    if (fast)
    {
        remove_vertex_fast(vertex(v, g), g);
    }
    else
    {
        remove_vertex(vertex(v, g), g);
    }
}


194
195
struct add_new_edge
{
196
197
198
    template <class Graph>
    void operator()(Graph& g, GraphInterface& gi, size_t s, size_t t,
                    python::object& new_e) const
199
    {
200
201
202
        std::shared_ptr<Graph> gp = retrieve_graph_view<Graph>(gi, g);
        auto e = add_edge(vertex(s, g), vertex(t, g), g).first;
        new_e = python::object(PythonEdge<Graph>(gp, e));
203
204
205
    }
};

206
python::object add_edge(GraphInterface& gi, size_t s, size_t t)
207
208
{
    python::object new_e;
209
210
    run_action<>()(gi, std::bind(add_new_edge(), placeholders::_1, std::ref(gi),
                                 s, t, std::ref(new_e)))();
211
212
213
214
215
216
    return new_e;
}

struct get_edge_descriptor
{
    template <class Graph>
217
    void operator()(const Graph&, const python::object& e,
218
                    typename GraphInterface::edge_t& edge,
219
                    bool& found)  const
220
    {
221
        PythonEdge<Graph>& pe = python::extract<PythonEdge<Graph>&>(e);
222
223
        pe.check_valid();
        edge = pe.get_descriptor();
224
        found = true;
225
226
227
    }
};

228
void remove_edge(GraphInterface& gi, const python::object& e)
229
{
230
    GraphInterface::edge_t de;
231
    bool found = false;
Tiago Peixoto's avatar
Tiago Peixoto committed
232
233
    run_action<>()(gi, std::bind(get_edge_descriptor(), placeholders::_1,
                                 std::ref(e), std::ref(de), std::ref(found)))();
234
    remove_edge(de, gi.get_graph());
235
    if (!found)
Tiago Peixoto's avatar
Tiago Peixoto committed
236
        throw ValueException("invalid edge descriptor");
237
238
}

239
240
241
struct get_edge_dispatch
{
    template <class Graph>
242
    void operator()(Graph& g, GraphInterface& gi, size_t s, size_t t,
243
244
                    bool all_edges, boost::python::list& es) const
    {
245
        std::shared_ptr<Graph> gp = retrieve_graph_view<Graph>(gi, g);
246
247
248
249
        for (auto e : out_edges_range(vertex(s, g), g))
        {
            if (target(e, g) == vertex(t, g))
            {
250
                es.append(PythonEdge<Graph>(gp, e));
251
252
253
254
255
256
257
                if (!all_edges)
                    break;
            }
        }
    }
};

258
python::object get_edge(GraphInterface& gi, size_t s, size_t t, bool all_edges)
259
260
{
    python::list es;
261
262
263
    run_action<>()(gi, std::bind(get_edge_dispatch(), placeholders::_1,
                                 std::ref(gi), s, t, all_edges,
                                 std::ref(es)))();
264
265
266
267
    return es;
}


268
269
struct get_degree_map
{
270
271
    template <class Graph, class DegS, class Weight>
    void operator()(const Graph& g, python::object& odeg_map, DegS deg, Weight weight) const
272
    {
273
        typedef typename detail::get_weight_type<Weight>::type weight_t;
Tiago Peixoto's avatar
Tiago Peixoto committed
274
        typedef typename mpl::if_<std::is_same<weight_t, size_t>, int32_t, weight_t>::type deg_t;
275
276
277
278
279
280
281
282

        typedef typename property_map_type::apply<deg_t,
                                                  GraphInterface::vertex_index_map_t>::type
            map_t;

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

283
        int i, N = num_vertices(g);
284
        #pragma omp parallel for default(shared) private(i) schedule(runtime) if (N > 100)
285
286
287
288
289
        for (i = 0; i < N; ++i)
        {
            typename graph_traits<Graph>::vertex_descriptor v = vertex(i, g);
            if (v == graph_traits<Graph>::null_vertex())
                continue;
290
            deg_map[v] = deg(v, g, weight);
291
        }
292
293

        odeg_map = python::object(PythonPropertyMap<map_t>(cdeg_map));
294
295
296
    }
};

297
python::object GraphInterface::degree_map(string deg, boost::any weight) const
298
299
{

300
301
    python::object deg_map;

302
303
    typedef mpl::push_back<edge_scalar_properties,
                           detail::no_weightS>::type weight_t;
304
305
    if (weight.empty())
        weight = detail::no_weightS();
306
307

    if (deg == "in")
308
        run_action<>()(const_cast<GraphInterface&>(*this),
Tiago Peixoto's avatar
Tiago Peixoto committed
309
310
                       std::bind(get_degree_map(), placeholders::_1,
                                 std::ref(deg_map), in_degreeS(), placeholders::_2), weight_t())
311
            (weight);
312
    else if (deg == "out")
313
        run_action<>()(const_cast<GraphInterface&>(*this),
Tiago Peixoto's avatar
Tiago Peixoto committed
314
315
                       std::bind(get_degree_map(), placeholders::_1,
                                 std::ref(deg_map), out_degreeS(), placeholders::_2), weight_t())
316
            (weight);
317
    else if (deg == "total")
318
        run_action<>()(const_cast<GraphInterface&>(*this),
Tiago Peixoto's avatar
Tiago Peixoto committed
319
320
                       std::bind(get_degree_map(), placeholders::_1,
                                 std::ref(deg_map), total_degreeS(), placeholders::_2), weight_t())
321
322
            (weight);
    return deg_map;
323
324
}

325
326
327
328
329
//
// Below are the functions with will properly register all the types to python,
// for every filter, type, etc.
//

330
331
332
// this will register all the Vertex/Edge classes to python
struct export_python_interface
{
333
334
335
    template <class Graph, class GraphViews>
    void operator()(Graph* gp, python::list vclasses,
                    python::list eclasses, GraphViews) const
336
337
    {
        using namespace boost::python;
338

339
340
        class_<PythonVertex<Graph>, bases<VertexBase>> vclass("Vertex", no_init);
        vclass
341
            .def("__in_degree", &PythonVertex<Graph>::get_in_degree,
342
                 "Return the in-degree.")
343
            .def("__weighted_in_degree", &PythonVertex<Graph>::get_weighted_in_degree,
344
                 "Return the weighted in-degree.")
345
            .def("__out_degree", &PythonVertex<Graph>::get_out_degree,
346
                 "Return the out-degree.")
347
            .def("__weighted_out_degree", &PythonVertex<Graph>::get_weighted_out_degree,
348
                 "Return the weighted out-degree.")
349
            .def("in_edges", &PythonVertex<Graph>::in_edges,
350
                 "Return an iterator over the in-edges.")
351
            .def("out_edges", &PythonVertex<Graph>::out_edges,
352
                 "Return an iterator over the out-edges.")
353
            .def("is_valid", &PythonVertex<Graph>::is_valid,
354
                 "Return whether the vertex is valid.")
355
356
357
358
359
            .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);
360
361
362
363
364

        vclasses.append(vclass);

        class_<PythonEdge<Graph>, bases<EdgeBase>> eclass("Edge", no_init);
        eclass
365
            .def("source", &PythonEdge<Graph>::get_source,
366
                 "Return the source vertex.")
367
            .def("target", &PythonEdge<Graph>::get_target,
368
                 "Return the target vertex.")
369
            .def("is_valid", &PythonEdge<Graph>::is_valid,
370
                 "Return whether the edge is valid.")
371
372
373
374
            .def("graph_ptr", &PythonVertex<Graph>::get_graph_ptr)
            .def("graph_type", &PythonVertex<Graph>::get_graph_type)
            .def("__str__", &PythonEdge<Graph>::get_string)
            .def("__hash__", &PythonEdge<Graph>::get_hash);
375

376
377
378
379
380
381
        boost::mpl::for_each<GraphViews>(std::bind(export_python_interface(),
                                                   gp, std::placeholders::_1,
                                                   std::ref(eclass)));

        eclasses.append(eclass);

382
        typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
383
384
385
386
387
388
389
        class_<PythonIterator<Graph, PythonVertex<Graph>, vertex_iterator> >
            ("VertexIterator", no_init)
            .def("__iter__", objects::identity_function())
            .def("__next__", &PythonIterator<Graph, PythonVertex<Graph>,
                                             vertex_iterator>::Next)
            .def("next", &PythonIterator<Graph, PythonVertex<Graph>,
                                         vertex_iterator>::Next);
390

391
        typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
392
        class_<PythonIterator<Graph, PythonEdge<Graph>,
393
394
                              edge_iterator> >("EdgeIterator", no_init)
            .def("__iter__", objects::identity_function())
395
            .def("__next__", &PythonIterator<Graph, PythonEdge<Graph>,
396
                                         edge_iterator>::Next)
397
            .def("next", &PythonIterator<Graph, PythonEdge<Graph>,
398
399
400
401
                                         edge_iterator>::Next);

        typedef typename graph_traits<Graph>::out_edge_iterator
            out_edge_iterator;
402
        class_<PythonIterator<Graph, PythonEdge<Graph>,
403
404
                              out_edge_iterator> >("OutEdgeIterator", no_init)
            .def("__iter__", objects::identity_function())
405
            .def("__next__", &PythonIterator<Graph, PythonEdge<Graph>,
406
                                             out_edge_iterator>::Next)
407
            .def("next", &PythonIterator<Graph, PythonEdge<Graph>,
408
409
410
411
                                         out_edge_iterator>::Next);

        typedef typename graph_traits<Graph>::directed_category
            directed_category;
Tiago Peixoto's avatar
Tiago Peixoto committed
412
413
        typedef typename std::is_convertible<directed_category,
                                             boost::directed_tag>::type is_directed;
414
415
416
        if (is_directed::value)
        {
            typedef typename in_edge_iteratorS<Graph>::type in_edge_iterator;
417
            class_<PythonIterator<Graph, PythonEdge<Graph>,
418
419
                                  in_edge_iterator> >("InEdgeIterator", no_init)
                .def("__iter__", objects::identity_function())
420
                .def("__next__", &PythonIterator<Graph, PythonEdge<Graph>,
421
                                                 in_edge_iterator>::Next)
422
                .def("next", &PythonIterator<Graph, PythonEdge<Graph>,
423
424
425
                                             in_edge_iterator>::Next);
        }
    }
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

    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);
    }
463
464
};

465
466
467
468
PythonPropertyMap<GraphInterface::vertex_index_map_t>
get_vertex_index(GraphInterface& g)
{
    return PythonPropertyMap<GraphInterface::vertex_index_map_t>
469
        (g.get_vertex_index());
470
471
472
}

PythonPropertyMap<GraphInterface::edge_index_map_t>
473
do_get_edge_index(GraphInterface& g)
474
475
{
    return PythonPropertyMap<GraphInterface::edge_index_map_t>
476
        (g.get_edge_index());
477
478
}

479
480
481
482
template <class ValueList>
struct add_edge_list
{
    template <class Graph>
483
484
    void operator()(Graph& g, python::object aedge_list,
                    python::object& eprops, bool& found) const
485
486
487
    {
        boost::mpl::for_each<ValueList>(std::bind(dispatch(), std::ref(g),
                                                  std::ref(aedge_list),
488
489
490
                                                  std::ref(eprops),
                                                  std::ref(found),
                                                  placeholders::_1));
491
492
493
494
495
    }

    struct dispatch
    {
        template <class Graph, class Value>
496
497
        void operator()(Graph& g, python::object& aedge_list,
                        python::object& oeprops, bool& found, Value) const
498
        {
499
500
            if (found)
                return;
501
502
503
504
505
506
507
            try
            {
                boost::multi_array_ref<Value, 2> edge_list = get_array<Value, 2>(aedge_list);

                if (edge_list.shape()[1] < 2)
                    throw GraphException("Second dimension in edge list must be of size (at least) two");

508
509
510
511
512
513
                typedef typename graph_traits<Graph>::edge_descriptor edge_t;
                vector<DynamicPropertyMapWrap<Value, edge_t>> eprops;
                python::stl_input_iterator<boost::any> iter(oeprops), end;
                for (; iter != end; ++iter)
                    eprops.emplace_back(*iter, writable_edge_properties());

514
                for (const auto& e : edge_list)
515
                {
516
517
                    size_t s = e[0];
                    size_t t = e[1];
518
519
                    while (s >= num_vertices(g) || t >= num_vertices(g))
                        add_vertex(g);
520
521
522
523
524
525
526
527
528
529
530
531
532
                    auto ne = add_edge(vertex(s, g), vertex(t, g), g).first;
                    for (size_t i = 0; i < e.size() - 2; ++i)
                    {
                        try
                        {
                            put(eprops[i], ne, e[i + 2]);
                        }
                        catch(bad_lexical_cast&)
                        {
                            throw ValueException("Invalid edge property value: " +
                                                 lexical_cast<string>(e[i + 2]));
                        }
                    }
533
534
535
536
537
538
539
540
                }
                found = true;
            }
            catch (invalid_numpy_conversion& e) {}
        }
    };
};

541
542
void do_add_edge_list(GraphInterface& gi, python::object aedge_list,
                      python::object eprops)
543
{
544
545
546
    typedef mpl::vector<bool, char, uint8_t, uint16_t, uint32_t, uint64_t,
                        int8_t, int16_t, int32_t, int64_t, uint64_t, double,
                        long double> vals_t;
547
    bool found = false;
548
549
    run_action<>()(gi, std::bind(add_edge_list<vals_t>(), placeholders::_1,
                                 aedge_list, std::ref(eprops),
550
551
552
553
554
                                 std::ref(found)))();
    if (!found)
        throw GraphException("Invalid type for edge list; must be two-dimensional with a scalar type");
}

555
556
557
558
559
template <class ValueList>
struct add_edge_list_hash
{
    template <class Graph, class VProp>
    void operator()(Graph& g, python::object aedge_list, VProp vmap,
560
                    bool& found, bool use_str, python::object& eprops) const
561
562
563
    {
        boost::mpl::for_each<ValueList>(std::bind(dispatch(), std::ref(g),
                                                  std::ref(aedge_list), std::ref(vmap),
564
565
                                                  std::ref(found), std::ref(eprops),
                                                  placeholders::_1));
566
567
568
        if (!found)
        {
            if (use_str)
569
                dispatch()(g, aedge_list, vmap, found, eprops, std::string());
570
            else
571
                dispatch()(g, aedge_list, vmap, found, eprops, python::object());
572
573
574
575
576
577
578
        }
    }

    struct dispatch
    {
        template <class Graph, class VProp, class Value>
        void operator()(Graph& g, python::object& aedge_list, VProp& vmap,
579
                        bool& found, python::object& oeprops, Value) const
580
581
582
583
584
585
586
587
588
589
590
        {
            if (found)
                return;
            try
            {
                boost::multi_array_ref<Value, 2> edge_list = get_array<Value, 2>(aedge_list);
                unordered_map<Value, size_t> vertices;

                if (edge_list.shape()[1] < 2)
                    throw GraphException("Second dimension in edge list must be of size (at least) two");

591
592
593
594
595
                typedef typename graph_traits<Graph>::edge_descriptor edge_t;
                vector<DynamicPropertyMapWrap<Value, edge_t>> eprops;
                python::stl_input_iterator<boost::any> iter(oeprops), end;
                for (; iter != end; ++iter)
                    eprops.emplace_back(*iter, writable_edge_properties());
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613

                auto get_vertex = [&] (const Value& r) -> size_t
                    {
                        auto iter = vertices.find(r);
                        if (iter == vertices.end())
                        {
                            auto v = add_vertex(g);
                            vertices[r] = v;
                            vmap[v] = lexical_cast<typename property_traits<VProp>::value_type>(r);
                            return v;
                        }
                        return iter->second;
                    };

                for (const auto& e : edge_list)
                {
                    size_t s = get_vertex(e[0]);
                    size_t t = get_vertex(e[1]);
614
615
616
617
618
619
620
621
622
623
624
625
626
                    auto ne = add_edge(vertex(s, g), vertex(t, g), g).first;
                    for (size_t i = 0; i < e.size() - 2; ++i)
                    {
                        try
                        {
                            put(eprops[i], ne, e[i + 2]);
                        }
                        catch(bad_lexical_cast&)
                        {
                            throw ValueException("Invalid edge property value: " +
                                                 lexical_cast<string>(e[i + 2]));
                        }
                    }
627
628
629
630
631
632
633
634
                }
                found = true;
            }
            catch (invalid_numpy_conversion& e) {}
        }

        template <class Graph, class VProp>
        void operator()(Graph& g, python::object& edge_list, VProp& vmap,
635
                        bool& found, python::object& oeprops, std::string) const
636
637
638
639
640
641
642
        {
            if (found)
                return;
            try
            {
                unordered_map<std::string, size_t> vertices;

643
644
645
646
647
648
                typedef typename graph_traits<Graph>::edge_descriptor edge_t;
                vector<DynamicPropertyMapWrap<python::object, edge_t>> eprops;
                python::stl_input_iterator<boost::any> piter(oeprops), pend;
                for (; piter != pend; ++piter)
                    eprops.emplace_back(*piter, writable_edge_properties());

649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
                auto get_vertex = [&] (const std::string& r) -> size_t
                    {
                        auto iter = vertices.find(r);
                        if (iter == vertices.end())
                        {
                            auto v = add_vertex(g);
                            vertices[r] = v;
                            vmap[v] = lexical_cast<typename property_traits<VProp>::value_type>(r);
                            return v;
                        }
                        return iter->second;
                    };

                python::stl_input_iterator<python::object> iter(edge_list), end;
                for (; iter != end; ++iter)
                {
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
                    const auto& row = *iter;

                    python::stl_input_iterator<python::object> eiter(row), eend;

                    size_t s = 0;
                    size_t t = 0;

                    typename graph_traits<Graph>::edge_descriptor e;
                    size_t i = 0;
                    for(; eiter != eend; ++eiter)
                    {
                        if (i >= eprops.size() + 2)
                            break;
                        const auto& val = *eiter;
                        switch (i)
                        {
                        case 0:
                            s = get_vertex(python::extract<std::string>(val));
                            while (s >= num_vertices(g))
                                add_vertex(g);
                            break;
                        case 1:
                            t = get_vertex(python::extract<std::string>(val));
                            while (t >= num_vertices(g))
                                add_vertex(g);
                            e = add_edge(vertex(s, g), vertex(t, g), g).first;
                            break;
                        default:
                            try
                            {
                                put(eprops[i - 2], e, val);
                            }
                            catch(bad_lexical_cast&)
                            {
                                throw ValueException("Invalid edge property value: " +
                                                     python::extract<string>(python::str(val))());
                            }
                        }
                        i++;
                    }
705
706
707
708
709
710
711
712
                }
                found = true;
            }
            catch (invalid_numpy_conversion& e) {}
        }

        template <class Graph, class VProp>
        void operator()(Graph& g, python::object& edge_list, VProp& vmap,
713
                        bool& found, python::object& oeprops, python::object) const
714
715
716
717
718
719
720
        {
            if (found)
                return;
            try
            {
                unordered_map<python::object, size_t> vertices;

721
722
723
724
725
726
                typedef typename graph_traits<Graph>::edge_descriptor edge_t;
                vector<DynamicPropertyMapWrap<python::object, edge_t>> eprops;
                python::stl_input_iterator<boost::any> piter(oeprops), pend;
                for (; piter != pend; ++piter)
                    eprops.emplace_back(*piter, writable_edge_properties());

727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
                auto get_vertex = [&] (const python::object& r) -> size_t
                    {
                        auto iter = vertices.find(r);
                        if (iter == vertices.end())
                        {
                            auto v = add_vertex(g);
                            vertices[r] = v;
                            vmap[v] = python::extract<typename property_traits<VProp>::value_type>(r);
                            return v;
                        }
                        return iter->second;
                    };

                python::stl_input_iterator<python::object> iter(edge_list), end;
                for (; iter != end; ++iter)
                {
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
782
                    const auto& row = *iter;

                    python::stl_input_iterator<python::object> eiter(row), eend;

                    size_t s = 0;
                    size_t t = 0;

                    typename graph_traits<Graph>::edge_descriptor e;
                    size_t i = 0;
                    for(; eiter != eend; ++eiter)
                    {
                        if (i >= eprops.size() + 2)
                            break;
                        const auto& val = *eiter;
                        switch (i)
                        {
                        case 0:
                            s = get_vertex(val);
                            while (s >= num_vertices(g))
                                add_vertex(g);
                            break;
                        case 1:
                            t = get_vertex(val);
                            while (t >= num_vertices(g))
                                add_vertex(g);
                            e = add_edge(vertex(s, g), vertex(t, g), g).first;
                            break;
                        default:
                            try
                            {
                                put(eprops[i - 2], e, val);
                            }
                            catch(bad_lexical_cast&)
                            {
                                throw ValueException("Invalid edge property value: " +
                                                     python::extract<string>(python::str(val))());
                            }
                        }
                        i++;
                    }
783
784
785
786
787
788
789
790
791
                }
                found = true;
            }
            catch (invalid_numpy_conversion& e) {}
        }
    };
};

void do_add_edge_list_hashed(GraphInterface& gi, python::object aedge_list,
792
793
                             boost::any& vertex_map, bool is_str,
                             python::object eprops)
794
795
796
797
798
799
800
801
{
    typedef mpl::vector<bool, char, uint8_t, uint16_t, uint32_t, uint64_t,
                        int8_t, int16_t, int32_t, int64_t, uint64_t, double,
                        long double> vals_t;
    bool found = false;
    run_action<graph_tool::detail::all_graph_views, boost::mpl::true_>()
        (gi, std::bind(add_edge_list_hash<vals_t>(), placeholders::_1,
                       aedge_list, placeholders::_2, std::ref(found),
802
                       is_str, std::ref(eprops)),
803
         writable_vertex_properties())(vertex_map);
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
}


struct add_edge_list_iter
{
    template <class Graph>
    void operator()(Graph& g, python::object& edge_list,
                    python::object& oeprops) const
    {
        typedef typename graph_traits<Graph>::edge_descriptor edge_t;
        vector<DynamicPropertyMapWrap<python::object, edge_t>> eprops;
        python::stl_input_iterator<boost::any> piter(oeprops), pend;
        for (; piter != pend; ++piter)
            eprops.emplace_back(*piter, writable_edge_properties());

        python::stl_input_iterator<python::object> iter(edge_list), end;
        for (; iter != end; ++iter)
        {
            const auto& row = *iter;
            python::stl_input_iterator<python::object> eiter(row), eend;

            size_t s = 0;
            size_t t = 0;

            typename graph_traits<Graph>::edge_descriptor e;
            size_t i = 0;
            for(; eiter != eend; ++eiter)
            {
                if (i >= eprops.size() + 2)
                    break;
                const auto& val = *eiter;
                switch (i)
                {
                case 0:
                    s = python::extract<size_t>(val);
                    while (s >= num_vertices(g))
                        add_vertex(g);
                    break;
                case 1:
                    t = python::extract<size_t>(val);
                    while (t >= num_vertices(g))
                        add_vertex(g);
                    e = add_edge(vertex(s, g), vertex(t, g), g).first;
                    break;
                default:
                    try
                    {
                        put(eprops[i - 2], e, val);
                    }
                    catch(bad_lexical_cast&)
                    {
                        throw ValueException("Invalid edge property value: " +
                                             python::extract<string>(python::str(val))());
                    }
                }
                i++;
            }
        }
    }
};

void do_add_edge_list_iter(GraphInterface& gi, python::object edge_list,
                           python::object eprops)
{
    run_action<>()
        (gi, std::bind(add_edge_list_iter(), placeholders::_1,
                       std::ref(edge_list), std::ref(eprops)))();
871
872
}

873

874
875
} // namespace graph_tool

876
877
// register everything

878
879
void export_python_properties();

880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
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;
}

897
void export_python_interface()
898
{
899
900
    using namespace boost::python;

901
    class_<VertexBase>("VertexBase", no_init);
902
    class_<EdgeBase>("EdgeBase", no_init);
903

Tiago Peixoto's avatar
Tiago Peixoto committed
904
    typedef boost::mpl::transform<graph_tool::detail::all_graph_views,
905
906
907
908
909
910
                                  boost::mpl::quote1<std::add_const> >::type const_graph_views;
    typedef boost::mpl::transform<graph_tool::detail::all_graph_views,
                                  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
911
    boost::mpl::for_each<graph_views>(std::bind(graph_tool::export_python_interface(),
912
913
                                                placeholders::_1, get_vlist(),
                                                get_elist(), graph_views()));
914
    export_python_properties();
915
916
917
918
919
920
    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> >);

921
922
923
924
925
926
927
    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);
    def("remove_edge", graph_tool::remove_edge);
928
    def("add_edge_list", graph_tool::do_add_edge_list);
929
    def("add_edge_list_hashed", graph_tool::do_add_edge_list_hashed);
930
    def("add_edge_list_iter", graph_tool::do_add_edge_list_iter);
931
    def("get_edge", get_edge);
932

933
    def("get_vertex_index", get_vertex_index);
934
    def("get_edge_index", do_get_edge_index);
935
936
937

    def("get_vlist", get_vlist);
    def("get_elist", get_elist);
938
}