使一个C ++类看起来像一个numpy数组使用swig [英] Make a C++ class look like a numpy array using swig

查看:464
本文介绍了使一个C ++类看起来像一个numpy数组使用swig的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有什么好的方法暴露一个C ++类,它提供了一个类似数组的接口用于numpy(scipy)?



通过数组类接口我的意思是像:

  // file:Arr.h 
class Arr {
public:
int n_rows;
int n_cols;
float * m_data;

Arr(int r,int c,float v);
virtual〜Arr();
float get(int i,int j);
void set(int i,int j,float v);

long data_addr(){
return(long)(m_data);
}
};

限制:





  • 该类将提供对原始存储的公开访问(可能通过函数),

  • 我不能在C ++头文件/源文件中添加特定于python的代码(我们不想让
    对C ++代码有一个Python依赖),因此任何对C ++
    端的修改必须通过SWIG完成(例如%extend )。



方法是在我的SWIG .i 文件
中添加一个 pythoncode >

 %pythoncode {
def arraylike_getitem(self,arg1,arg2):
# b $ b#相当复杂,但涉及到:
#1.为返回值构造一个未初始化的numpy数组
#2.迭代切片指示的索引,
#3.调用self .getValue为每个索引对
#4.返回数组

#将函数添加到ArrayLike类
Arr .__ getitem __ = arraylike_getitem
%}

其中 ArrayLike 数值数据(作为一个平面数组),
并提供成员函数来获取/设置单个值。



主要缺点是步骤1:必须复制我取的我的c数组类的任何切片
。 (主要的优点是,通过返回
numpy数组对象,我知道我可以在任何numpy操作中使用它。)



想象两种改进方法:


  1. 添加(通过SWIG %extend )功能到c类,或者

  2. 让python函数返回一个数组切片代理对象,


$ b b

我的主要挂机不知道对象需要什么接口(有效地)实现以便像numpy数组一样嘎嘎声。



测试



这是我的测试设置:

  /file:Arr.h 
class Arr {
public:
int n_rows;
int n_cols;
float * m_data;

Arr(int r,int c,float v);
virtual〜Arr();
float get(int i,int j);
void set(int i,int j,float v);

long data_addr(){
return(long)(m_data);
}
};

// ---------------------------------------- -------------------

//文件Arr.cpp
#includeArr.h

Arr :: Arr(int r,int c,float v):n_rows(r),n_cols(c),m_data(0){
m_data = new float [r * c]
for(int i = 0; i m_data [i] = v;
}
}
Arr ::〜Arr(){
delete [] m_data;
}

float Arr :: get(int i,int j){
return m_data [i * n_cols + j]
}
void Arr :: set(int i,int j,float v){
m_data [i * n_cols + j] = v;
}

// ----------------------------------- ---------------------------------
//file:arr.i
%模块arr

%{
#includeArr.h
#include< /usr/lib64/python2.7/site-packages/numpy/core/include/numpy /ndarrayobject.h>
#include< python2.7 / Python.h>
%}

%includeArr.h


%pythoncode {

#构造问题):允许
#arr对象和numpy数组之间的操作(例如numpy_array + arr_object是OK)
#但不允许切片(例如numpy_array [:: 2,:: 2] + arr_objec [:: 2,:: 2])
#TODO:找出如何获取没有副本内存的分片
def arr_interface_map(self):
res = {'shape':( self。 n_rows,self.n_cols),'typestr':'< f4','data':self.data_addr(),0),'version':3}
return res

Arr。_ array_interface __ = property(arr_interface_map)


}

// ------------------- --------------------------------------
#file:Makefile
INCLUDE_FLAGS = -I / usr / include / python2.7

arr_wrap.cpp:arr.i Arr.h
swig -c ++ -python -o $ @ $ {INCLUDE_FLAGS} arr.i

_arr.so:arr_wrap.o Arr.o
g ++ -shared -o _arr.so arr_wrap.o Arr.o

clean:
rm -f * .o * _wrap.cpp * .so

all:_arr.so

如果我可以得到 Arr 类使用 numpy ,那么我已经成功了。 / p>

编辑
这个相关问题它看起来像 __ array_interface __ 将是解决方案的一部分)

解决方案

如果 n_cols n_rows 是(实际上)不可变的,你最好的做法是简单地创建一个实数numpy数组,给它 m_data code>(n_rows,n_cols)作为shape。这样,您将获得所有numpy数组设施,而不需要任何复制,也不必在您自己的代码中重新实现它们(这将是一个很多的quacking模仿)。

  PyObject * array_like_to_numpy(ArrayLike& obj)
{
npy_intp dims [] = {obj.n_rows,obj.n_cols};
return PyArray_SimpleNewFromData(2,dims,NPY_FLOAT,obj.m_data);
}

当然,这不会像写的那样工作,因为 m_data 成员受保护。但是它是一个好主意,使其公开或提供访问器来检索它(或继承 ArrayLike 并在您的子类中提供此类功能)。


What's a good way to expose a C++ class that provides an array-like interface for use with numpy (scipy)?

By array-like interface I mean something like:

//file:Arr.h
class Arr{
public:
    int n_rows;
    int n_cols;
    float* m_data;

    Arr(int r, int c, float v);
    virtual ~Arr();
    float get(int i, int j);
    void set(int i, int j, float v);

    long data_addr(){
        return (long)(m_data);
    }
};

Constraints:

  • I only care about classes that store their underlying data as contiguous flat arrays,
  • The class will provide public access to the raw storage (probably through a function),
  • I cannot add python specific code to the C++ header/source files (we do not want to have a Python dependency for the C++ code) thus any modifications on the C++ side have to be done via SWIG (e.g. %extend).

My current approach is to put a pythoncode block in my SWIG .i file that looks something like

%pythoncode{
def arraylike_getitem(self, arg1,arg2 ):
   # the actual implementation to handle slices
   # is pretty complicated but involves:
   # 1. constructing an uninitialized  numpy array for return value
   # 2. iterating over the indices indicated by the slices,
   # 3. calling self.getValue for each of the index pairs,
   # 4. returning the array

# add the function to the ArrayLike class
Arr.__getitem__=arraylike_getitem
%}

where ArrayLike is the C++ class that holds the numerical data (as a flat array), and provides member functions to get/set individual values.

The main drawback is step 1. above: I have to make a copy of any slice that I take of my c-array class. (The main advantage is that by returning a numpy array object, I know that I can use it in any numpy operations that I want.)

I can imagine two approaches for improving this:

  1. Adding (via SWIG %extend) additional functionality to the c class, and or
  2. having the python function return an array-slice proxy object,

My main hang-up is not knowing what interface an object needs to (efficiently) implement in order to quack like a numpy array.

Test Case

Here's my test setup:

//file:Arr.h
class Arr{
public:
    int n_rows;
    int n_cols;
    float* m_data;

    Arr(int r, int c, float v);
    virtual ~Arr();
    float get(int i, int j);
    void set(int i, int j, float v);

    long data_addr(){
        return (long)(m_data);
    }
};

//-----------------------------------------------------------

//file Arr.cpp
#include "Arr.h"

Arr::Arr(int r, int c, float v): n_rows(r), n_cols(c), m_data(0){
    m_data=new float[ r*c ];
    for( int i=0; i<r*c; ++i){
        m_data[i]=v;
    }
}  
Arr::~Arr(){
    delete[] m_data;
}

float Arr::get(int i, int j){
    return m_data[ i*n_cols+j];
}
void Arr::set(int i, int j, float v){
    m_data[i*n_cols+j]=v;
}

//--------------------------------------------------------------------
//file:arr.i
%module arr

%{
#include "Arr.h"
#include </usr/lib64/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h>
#include <python2.7/Python.h>
%}

%include "Arr.h"


%pythoncode{

# Partial solution (developed in constructing the question): allows operations between 
# arr objects and numpy arrays (e.g. numpy_array+arr_object is OK)
# but does not allow slicing (e.g. numpy_array[::2,::2]+arr_objec[::2,::2])
# TODO: figure out how to get slices without copy memory
def arr_interface_map(self):
    res={ 'shape':(self.n_rows, self.n_cols), 'typestr':'<f4', 'data': self.data_addr(),0), 'version':3 }
    return res

Arr.__array_interface__=property( arr_interface_map )


}

//---------------------------------------------------------
#file: Makefile
INCLUDE_FLAGS = -I/usr/include/python2.7 

arr_wrap.cpp: arr.i Arr.h
     swig -c++ -python -o $@ ${INCLUDE_FLAGS} arr.i

_arr.so: arr_wrap.o Arr.o
    g++ -shared -o _arr.so arr_wrap.o Arr.o 

clean:
    rm -f *.o *_wrap.cpp *.so

all: _arr.so

If I can get this Arr class to work with numpy, then I've succeeded.

Edit: From this related question it looks like __array_interface__ will be part of the solution (TBD: how to use it?)

解决方案

If n_cols and n_rows are (effectively) immutable, your best course of action is to simply create a real numpy array, giving it m_data as storage and (n_rows, n_cols) as shape. That way you will get all the numpy array facilities without any copying and without having to reimplement them in your own code (which would be a lot of quacking to imitate).

PyObject* array_like_to_numpy(ArrayLike& obj)
{
    npy_intp dims[] = { obj.n_rows, obj.n_cols };
    return PyArray_SimpleNewFromData(2, dims, NPY_FLOAT, obj.m_data);
}

Of course, this won't work as written, since your m_data member is protected. But it would be a good idea to either make it public or provide an accessor to retrieve it (or inherit from ArrayLike and provide such functionality in your subclass).

这篇关于使一个C ++类看起来像一个numpy数组使用swig的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆