Skip to content

Commit 0e21e53

Browse files
committed
add support for Eigen arrays + move SWIG boilerplate to own file
1 parent 7965028 commit 0e21e53

File tree

6 files changed

+191
-102
lines changed

6 files changed

+191
-102
lines changed

config.i

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
2+
3+
%include "numpy.i"
4+
%init %{
5+
import_array();
6+
%}
7+
%include <eigen.i>
8+
9+
// ------------------------------------------------
10+
// vector typemaps
11+
// ------------------------------------------------
12+
13+
%define %np_vector_typemaps(DTYPE, NPY_DPTYE)
14+
15+
namespace std {
16+
// hmm...apparently telling SWIG to try to optimize this breaks it
17+
// %typemap(out, fragment="NumPy_Fragments", optimal="1") vector<DTYPE> {
18+
%typemap(out, fragment="NumPy_Fragments") vector<DTYPE> {
19+
// create python array of appropriate shape
20+
npy_intp sz = static_cast<npy_intp>($1.size());
21+
npy_intp dims[] = {sz};
22+
PyObject* out_array = PyArray_SimpleNew(1, dims, NPY_DPTYE);
23+
24+
if (! out_array) {
25+
PyErr_SetString(PyExc_ValueError,
26+
"vector wrap: unable to create the output array.");
27+
return NULL;
28+
}
29+
30+
// copy data from vect into numpy array
31+
DTYPE* out_data = (DTYPE*) array_data(out_array);
32+
for (size_t i = 0; i < sz; i++) {
33+
out_data[i] = static_cast<DTYPE>($1[i]);
34+
}
35+
36+
$result = out_array;
37+
}
38+
}
39+
40+
%enddef
41+
42+
%np_vector_typemaps(int, NPY_INT)
43+
%np_vector_typemaps(long, NPY_LONG)
44+
%np_vector_typemaps(float, NPY_FLOAT)
45+
%np_vector_typemaps(double, NPY_DOUBLE)
46+
// %np_vector_typemaps(SimpleStruct*, NPY_OBJECT) // breaks
47+
48+
// ------------------------------------------------
49+
// eigen typemaps
50+
// ------------------------------------------------
51+
52+
%eigen_typemaps(MatrixXd)
53+
%eigen_typemaps(VectorXd)
54+
%eigen_typemaps(ArrayXXd)
55+
%eigen_typemaps(ArrayXd)
56+
57+
%eigen_typemaps(MatrixXf)
58+
%eigen_typemaps(VectorXf)
59+
%eigen_typemaps(ArrayXXf)
60+
%eigen_typemaps(ArrayXf)
61+
62+
%eigen_typemaps(MatrixXi)
63+
%eigen_typemaps(VectorXi)
64+
%eigen_typemaps(ArrayXXi)
65+
%eigen_typemaps(ArrayXi)
66+
67+
// ------------------------------------------------
68+
// raw c array typemaps
69+
// ------------------------------------------------
70+
71+
// apply numpy typemaps based on arg types + names
72+
%apply (double* INPLACE_ARRAY1, int DIM1) {(double* inVec, int len)};
73+
74+
%apply (double* IN_ARRAY1, int DIM1) {(double* v, int len)};
75+
%apply (double* IN_ARRAY1, int DIM1) {(double* ar, int len)};
76+
%apply (const double* IN_ARRAY1, int DIM1) {(const double* ar, int len)};
77+
%apply (double* IN_ARRAY1, int DIM1) {(double* v, int inLen)};
78+
%apply (double* IN_ARRAY1, int DIM1) {(double* v1, int len1)};
79+
%apply (double* IN_ARRAY1, int DIM1) {(double* v2, int len2)};
80+
81+
%apply (int* ARGOUT_ARRAY1, int DIM1) {(int* outVec, int len)};
82+
%apply (int* ARGOUT_ARRAY1, int DIM1) {(int* outVec, int outLen)};
83+
%apply (double* ARGOUT_ARRAY1, int DIM1) {(double* outVec, int len)};
84+
%apply (double* ARGOUT_ARRAY1, int DIM1) {(double* outVec, int outLen)};
85+
// %apply (int DIM1, double* ARGOUT_ARRAY1) {(int len, double* outVec)};

eigen.i

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
/* Additional credit to Martin Felis:
3737
* https://bitbucket.org/MartinFelis/eigen3swig/src
3838
*
39-
* Plus some minor modifications of my own (Davis Blalock, 2016-3-7)
39+
* Plus some modifications of my own (Davis Blalock, 2016-3-7);
40+
* in particular, I've modified this to work with both Eigen matrices
41+
* and arrays.
4042
*/
4143

4244
%{
@@ -56,7 +58,7 @@
5658
template <typename T> int NumPyType() {return -1;};
5759

5860
template <class Derived>
59-
void ConvertFromNumpyToEigenMatrix(Eigen::MatrixBase<Derived>* out, PyObject* in)
61+
void ConvertFromNumpyToEigen(Eigen::DenseBase<Derived>* out, PyObject* in)
6062
{
6163
int rows = 0;
6264
int cols = 0;
@@ -125,7 +127,7 @@
125127

126128
// Copies values from Eigen type into an existing NumPy type
127129
template <class Derived>
128-
void CopyFromEigenToNumPyMatrix(PyObject* out, Eigen::MatrixBase<Derived>* in)
130+
void CopyFromEigenToNumPy(PyObject* out, Eigen::DenseBase<Derived>* in)
129131
{
130132
int rows = 0;
131133
int cols = 0;
@@ -193,7 +195,7 @@
193195
};
194196

195197
template <class Derived>
196-
void ConvertFromEigenToNumPyMatrix(PyObject** out, Eigen::MatrixBase<Derived>* in)
198+
void ConvertFromEigenToNumPy(PyObject** out, Eigen::DenseBase<Derived>* in)
197199
{
198200
// vector (1D)
199201
if (in->cols() == 1) {
@@ -233,27 +235,27 @@
233235
%typemap(argout, fragment="Eigen_Fragments") CLASS &
234236
{
235237
// Argout: &
236-
CopyFromEigenToNumPyMatrix<CLASS>($input, $1);
238+
CopyFromEigenToNumPy<CLASS>($input, $1);
237239
}
238240

239241
// In: (nothing: no constness)
240242
%typemap(in, fragment="Eigen_Fragments") CLASS (CLASS temp)
241243
{
242-
ConvertFromNumpyToEigenMatrix<CLASS>(&temp, $input);
244+
ConvertFromNumpyToEigen<CLASS>(&temp, $input);
243245
$1 = temp;
244246
}
245247
// In: const&
246248
%typemap(in, fragment="Eigen_Fragments") CLASS const& (CLASS temp)
247249
{
248250
// In: const&
249-
ConvertFromNumpyToEigenMatrix<CLASS>(&temp, $input);
251+
ConvertFromNumpyToEigen<CLASS>(&temp, $input);
250252
$1 = &temp;
251253
}
252254
// In: & (not yet implemented)
253255
%typemap(in, fragment="Eigen_Fragments") CLASS & (CLASS temp)
254256
{
255257
// In: non-const&
256-
ConvertFromNumpyToEigenMatrix<CLASS>(&temp, $input);
258+
ConvertFromNumpyToEigen<CLASS>(&temp, $input);
257259

258260
$1 = &temp;
259261
}
@@ -271,17 +273,17 @@
271273
// Out: (nothing: no constness)
272274
%typemap(out, fragment="Eigen_Fragments") CLASS
273275
{
274-
ConvertFromEigenToNumPyMatrix<CLASS>(&$result, &$1);
276+
ConvertFromEigenToNumPy<CLASS>(&$result, &$1);
275277
}
276278
// Out: const
277279
%typemap(out, fragment="Eigen_Fragments") CLASS const
278280
{
279-
ConvertFromEigenToNumPyMatrix<CLASS>(&$result, &$1);
281+
ConvertFromEigenToNumPy<CLASS>(&$result, &$1);
280282
}
281283
// Out: const&
282284
%typemap(out, fragment="Eigen_Fragments") CLASS const&
283285
{
284-
ConvertFromEigenToNumPyMatrix<CLASS>(&$result, $1);
286+
ConvertFromEigenToNumPy<CLASS>(&$result, $1);
285287
}
286288
// Out: & (not yet implemented)
287289
%typemap(out, fragment="Eigen_Fragments") CLASS &

example.i

Lines changed: 4 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,17 @@
11
%module example
22

3+
// include files that SWIG will need to see
34
%{
45
#define SWIG_FILE_WITH_INIT
56
#include <vector>
67
#include "src/include/public_interface.hpp"
78
#include "src/include/public_interface_eigen.hpp"
89
%}
910

10-
%include "numpy.i"
11-
%init %{
12-
import_array();
13-
%}
14-
%include <eigen.i>
15-
16-
// ------------------------------------------------
17-
// vector typemaps
18-
// ------------------------------------------------
19-
20-
%define %np_vector_typemaps(DTYPE, NPY_DPTYE)
21-
22-
namespace std {
23-
// hmm...apparently telling SWIG to try to optimize this breaks it
24-
// %typemap(out, fragment="NumPy_Fragments", optimal="1") vector<DTYPE> {
25-
%typemap(out, fragment="NumPy_Fragments") vector<DTYPE> {
26-
// create python array of appropriate shape
27-
npy_intp sz = static_cast<npy_intp>($1.size());
28-
npy_intp dims[] = {sz};
29-
PyObject* out_array = PyArray_SimpleNew(1, dims, NPY_DPTYE);
30-
31-
if (! out_array) {
32-
PyErr_SetString(PyExc_ValueError,
33-
"vector wrap: unable to create the output array.");
34-
return NULL;
35-
}
36-
37-
// copy data from vect into numpy array
38-
DTYPE* out_data = (DTYPE*) array_data(out_array);
39-
for (size_t i = 0; i < sz; i++) {
40-
out_data[i] = static_cast<DTYPE>($1[i]);
41-
}
42-
43-
$result = out_array;
44-
}
45-
}
46-
47-
%enddef
48-
49-
%np_vector_typemaps(int, NPY_INT)
50-
%np_vector_typemaps(long, NPY_LONG)
51-
%np_vector_typemaps(float, NPY_FLOAT)
52-
%np_vector_typemaps(double, NPY_DOUBLE)
53-
// %np_vector_typemaps(SimpleStruct*, NPY_OBJECT) // breaks
54-
55-
// ------------------------------------------------
56-
// eigen typemaps
57-
// ------------------------------------------------
58-
59-
%eigen_typemaps(MatrixXd)
60-
%eigen_typemaps(VectorXd)
61-
// %eigen_typemaps(ArrayXd)
62-
%eigen_typemaps(MatrixXf)
63-
%eigen_typemaps(VectorXf)
64-
%eigen_typemaps(MatrixXi)
65-
%eigen_typemaps(VectorXi)
66-
67-
// ------------------------------------------------
68-
// raw c array typemaps
69-
// ------------------------------------------------
70-
71-
// apply numpy typemaps based on arg types + names
72-
%apply (double* INPLACE_ARRAY1, int DIM1) {(double* inVec, int len)};
73-
74-
%apply (double* IN_ARRAY1, int DIM1) {(double* v, int len)};
75-
%apply (double* IN_ARRAY1, int DIM1) {(double* ar, int len)};
76-
%apply (const double* IN_ARRAY1, int DIM1) {(const double* ar, int len)};
77-
%apply (double* IN_ARRAY1, int DIM1) {(double* v, int inLen)};
78-
%apply (double* IN_ARRAY1, int DIM1) {(double* v1, int len1)};
79-
%apply (double* IN_ARRAY1, int DIM1) {(double* v2, int len2)};
80-
81-
%apply (int* ARGOUT_ARRAY1, int DIM1) {(int* outVec, int len)};
82-
%apply (int* ARGOUT_ARRAY1, int DIM1) {(int* outVec, int outLen)};
83-
%apply (double* ARGOUT_ARRAY1, int DIM1) {(double* outVec, int len)};
84-
%apply (double* ARGOUT_ARRAY1, int DIM1) {(double* outVec, int outLen)};
85-
// %apply (int DIM1, double* ARGOUT_ARRAY1) {(int len, double* outVec)};
11+
// include other SWIG interface files
12+
%include <config.i>
8613

14+
// tell SWIG to wrap the relevant files
8715
%include "src/include/public_interface.hpp"
8816
%include "src/include/public_interface_eigen.hpp"
8917

pyWrapper/test_example.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,26 +165,36 @@ def printVar(varName, var):
165165

166166
M_out = example.createEigenMat()
167167
v_out = example.createEigenVect()
168-
# A_out = example.createEigenArray()
168+
A_out = example.createEigenArray()
169+
u_out = example.createEigenArrayVect()
169170

170171
assertEqual(A, M_out)
171172
assertEqual(v, v_out)
172-
# assertEqual(A, A_out) # TODO typemaps for arrays
173+
assertEqual(A, A_out)
174+
assertEqual(v, u_out)
173175

174176
# ------------------------ floats
175177
M_out = example.createEigenMatf()
176178
v_out = example.createEigenVectf()
179+
A_out = example.createEigenArrayf()
180+
u_out = example.createEigenArrayVectf()
177181

178182
assertEqual(A, M_out)
179183
assertEqual(v, v_out)
184+
assertEqual(A, A_out)
185+
assertEqual(v, u_out)
180186

181187
# ------------------------ ints
182188
A = A.astype(np.int)
183189
v = v.astype(np.int)
184190

185191
M_out = example.createEigenMati()
186192
v_out = example.createEigenVecti()
193+
A_out = example.createEigenArrayi()
194+
u_out = example.createEigenArrayVecti()
187195

188196
assertEqual(A, M_out)
189197
assertEqual(v, v_out)
198+
assertEqual(A, A_out)
199+
assertEqual(v, u_out)
190200

0 commit comments

Comments
 (0)