numpy_bind_old.hh 7.11 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-2014 Tiago de Paula Peixoto <tiago@skewed.de>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//
// 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/>.

#ifndef NUMPY_BIND_OLD_HH
#define NUMPY_BIND_OLD_HH

#include <vector>
#include <boost/python.hpp>

#include <boost/array.hpp>
#define BOOST_DISABLE_ASSERTS
#include <boost/multi_array.hpp>

#include <boost/type_traits.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/map.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/for_each.hpp>

using namespace std;
36
37
38

typedef boost::mpl::map<
    boost::mpl::pair<bool, boost::mpl::int_<NPY_BOOL> >,
39
40
    boost::mpl::pair<int8_t, boost::mpl::int_<NPY_INT8> >,
    boost::mpl::pair<uint8_t, boost::mpl::int_<NPY_UINT8> >,
41
    boost::mpl::pair<int16_t, boost::mpl::int_<NPY_INT16> >,
42
    boost::mpl::pair<uint16_t, boost::mpl::int_<NPY_UINT16> >,
43
    boost::mpl::pair<int32_t, boost::mpl::int_<NPY_INT32> >,
44
    boost::mpl::pair<uint32_t, boost::mpl::int_<NPY_UINT32> >,
45
46
    boost::mpl::pair<int64_t, boost::mpl::int_<NPY_INT64> >,
    boost::mpl::pair<uint64_t, boost::mpl::int_<NPY_UINT64> >,
47
    boost::mpl::pair<float, boost::mpl::int_<NPY_FLOAT> >,
48
    boost::mpl::pair<double, boost::mpl::int_<NPY_DOUBLE> >,
49
50
51
52
    boost::mpl::pair<long double, boost::mpl::int_<NPY_LONGDOUBLE> >,
    boost::mpl::pair<std::complex<float>, boost::mpl::int_<NPY_CFLOAT> >,
    boost::mpl::pair<std::complex<double>, boost::mpl::int_<NPY_CDOUBLE> >,
    boost::mpl::pair<std::complex<long double>, boost::mpl::int_<NPY_CLONGDOUBLE> > 
53
54
55
    > numpy_types;

template <class ValueType>
56
boost::python::object wrap_vector_owned(vector<ValueType>& vec)
57
{
58
    int val_type = boost::mpl::at<numpy_types,ValueType>::type::value;
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    npy_intp size[1];
    size[0] = vec.size();
    PyArrayObject* ndarray;
    if (vec.empty())
    {
        ndarray = (PyArrayObject*) PyArray_SimpleNew(1, size, val_type);
    }
    else
    {
        ValueType* new_data = new ValueType[vec.size()];
        memcpy(new_data, &vec[0], vec.size()*sizeof(ValueType));
        ndarray = (PyArrayObject*) PyArray_SimpleNewFromData(1, size, val_type,
                                                             new_data);
    }
    ndarray->flags = NPY_ALIGNED | NPY_C_CONTIGUOUS | NPY_OWNDATA |
        NPY_WRITEABLE;
75
76
    boost::python::handle<> x((PyObject*) ndarray);
    boost::python::object o(x);
77
78
79
80
    return o;
}

template <class ValueType>
81
boost::python::object wrap_vector_not_owned(vector<ValueType>& vec)
82
83
{
    PyArrayObject* ndarray;
84
    int val_type = boost::mpl::at<numpy_types,ValueType>::type::value;
85
86
87
88
89
90
91
    npy_intp size = vec.size();
    if (vec.empty())
        return wrap_vector_owned(vec); // return an _owned_ array of size one.
    else
        ndarray = (PyArrayObject*) PyArray_SimpleNewFromData(1, &size, val_type,
                                                             &vec[0]);
    ndarray->flags = NPY_ALIGNED | NPY_C_CONTIGUOUS | NPY_WRITEABLE;
92
93
    boost::python::handle<> x((PyObject*) ndarray);
    boost::python::object o(x);
94
95
96
97
98
    return o;
}


template <class ValueType, int Dim>
99
boost::python::object wrap_multi_array_owned(boost::multi_array<ValueType,Dim>& array)
100
101
102
{
    ValueType* new_data = new ValueType[array.num_elements()];
    memcpy(new_data, array.data(), array.num_elements()*sizeof(ValueType));
103
    int val_type = boost::mpl::at<numpy_types,ValueType>::type::value;
104
105
106
107
108
109
110
111
    npy_intp shape[Dim];
    for (int i = 0; i < Dim; ++i)
        shape[i] = array.shape()[i];
    PyArrayObject* ndarray =
        (PyArrayObject*) PyArray_SimpleNewFromData(Dim, shape, val_type,
                                                   new_data);
    ndarray->flags = NPY_ALIGNED | NPY_C_CONTIGUOUS | NPY_OWNDATA |
        NPY_WRITEABLE;
112
113
    boost::python::handle<> x((PyObject*) ndarray);
    boost::python::object o(x);
114
115
116
117
    return o;
}

template <class ValueType, int Dim>
118
boost::python::object wrap_multi_array_not_owned(boost::multi_array<ValueType,Dim>& array)
119
{
120
    int val_type = boost::mpl::at<numpy_types,ValueType>::type::value;
121
122
123
124
    PyArrayObject* ndarray =
        (PyArrayObject*) PyArray_SimpleNewFromData(Dim, array.shape(), val_type,
                                                   array.origin());
    ndarray->flags = NPY_ALIGNED | NPY_C_CONTIGUOUS | NPY_WRITEABLE;
125
126
    boost::python::handle<> x((PyObject*) ndarray);
    boost::python::object o(x);
127
128
129
130
131
    return o;
}

// get multi_array_ref from numpy ndarrays

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
template <class ValueType, size_t dim>
class numpy_multi_array: public boost::multi_array_ref<ValueType,dim>
{
    typedef boost::multi_array_ref<ValueType,dim> base_t;
public:
    template <class ExtentList, class StrideList>
    explicit numpy_multi_array(typename base_t::element* data,
                               const ExtentList& sizes,
                               const StrideList& strides)
        :base_t(data, sizes)
    {
        for (int i = 0; i < dim; ++i)
            base_t::stride_list_[i] = strides[i];
    }
};

148
149
150
151
152
153
154
155
156
157
158
struct invalid_numpy_conversion:
    public std::exception
{
    string _error;
public:
    invalid_numpy_conversion(const string& error) {_error = error;}
    ~invalid_numpy_conversion() throw () {}
    const char * what () const throw () {return _error.c_str();}
};

template <class ValueType, size_t dim>
159
boost::multi_array_ref<ValueType,dim> get_array(boost::python::object points)
160
161
162
163
164
165
{
    PyArrayObject* pa = (PyArrayObject*) points.ptr();

    if (pa->nd != dim)
        throw invalid_numpy_conversion("invalid array dimension!");

166
    if (boost::mpl::at<numpy_types,ValueType>::type::value != pa->descr->type_num)
167
    {
168
169
170
171
        using boost::python::detail::gcc_demangle;
        boost::python::handle<> x((PyObject*)  pa->descr->typeobj);
        boost::python::object dtype(x);
        string type_name = boost::python::extract<string>(boost::python::str(dtype));
172
        string error = "invalid array value type: " + type_name;
Tiago Peixoto's avatar
Tiago Peixoto committed
173
        error += " (id: " + boost::lexical_cast<string>(pa->descr->type_num) + ")";
174
        error += ", wanted: " + string(gcc_demangle(typeid(ValueType).name()));
175
        error += " (id: " + boost::lexical_cast<string>(boost::mpl::at<numpy_types,ValueType>::type::value) + ")";
176
177
178
179
180
181
        throw invalid_numpy_conversion(error);
    }

    vector<size_t> shape(pa->nd);
    for (int i = 0; i < pa->nd; ++i)
        shape[i] = pa->dimensions[i];
182
183
184
185
186
187
188

    vector<size_t> stride(dim);
    for (size_t i = 0; i < dim; ++i)
        stride[i] = pa->strides[i] / sizeof(ValueType);

    return numpy_multi_array<ValueType,dim>((ValueType *) pa->data,
                                            shape, stride);
189
190
191
}

#endif // NUMPY_BIND_OLD_HH