Unknown type name 'PyModuleDef' - python

I'm working with Python and C++ integration, and I'm on MacOS.
When I use all of the PyObject methods they all seem to work but none of the PyModule methods seem to be working.
This is my code:
#include <iostream>
#include <cmath>
#include <vector>
#if PY_MAJOR_VERSION >= 3
#define PY3K
#endif
#ifdef __APPLE__
#include <Python/Python.h>
#else
#include <Python.h>
#endif
#define space " "
#define epoch int(1000)
using namespace std;
double hypothesis(const std::vector<double>& b_val, double x){
return b_val[0] + b_val[1] * x;
}
std::vector<double> regression(const std::vector<double>& x, const std::vector<double>& y,
int epochs, double learning_rate){
if(!epochs) epochs = epoch;
double _m = 0;
double _b = 0;
std::vector<double> _values(2);
int N = x.size();
for(int i = 0; i < epochs; i++){
_values[0] = _b, _values[1] = _m;
double dm = 0;
double db = 0;
double cost = 0;
for(int j = 0; j < N; j++){
double p = hypothesis(_values, x[j]);
cost += pow(y[j] - p, 2);
dm += (-2.0 / N) * (x[j] * (y[j] - p));
db += (-2.0 / N) * (y[j] - p);
}
cost /= N;
_m = _m - (learning_rate * dm);
_b = _b - (learning_rate * db);
if ((i + 1) % 100 == 0)
std::cout << "Epoch: " << (i + 1) << " Cost: " << cost << std::endl;
}
std::vector<double> result(2);
result[0] = _m, result[1] = _b;
return result;
}
static PyObject * fit(PyObject * self, PyObject * args){
PyObject *x;
PyObject *y;
double learning_rate;
int epochs;
int N;
if(!PyArg_ParseTuple(args, "00di0i", &x, &y, &epochs, &learning_rate, &N)){
return nullptr;
}
std::vector<double> _x(N), _y(N);
for(int i = 0; i < N; i++){
_x[i] = PyFloat_AsDouble(PyList_GetItem(x, (Py_ssize_t)i));
_y[i] = PyFloat_AsDouble(PyList_GetItem(y, (Py_ssize_t)i));
}
std::vector<double> _result = regression(_x, _y, epochs, learning_rate);
PyObject *result = PyTuple_New(2);
for(int i = 0; i < 2; i++){
PyTuple_SetItem(result, i, PyFloat_FromDouble(_result[i]));
}
return Py_BuildValue("s", result);
}
static PyMethodDef linreg_methods[] = {
{"fit", (PyCFunction)fit, METH_VARARGS, "Linear Regression"},
{nullptr}
};
static PyModuleDef linear_regression = {
PyModuleDef_HEAD_INIT,
"linear_regression"
"Linear Regression",
-1,
linreg_methods
};
PyMODINIT_FUNC PyInit_linear_regression(void){
return PyModule_Create(&linear_regression);
}
The output I get is error: unknown type name 'PyModuleDef'.
I can't seem to understand what the problem is.

You will need to make sure the included Python.h is from the Python3.x build, and that you link to the corresponding library, for example, on Linux that would be:
g++ my_module.cpp -I/usr/include/python3.8/ -lpython3.8
But on MacOS this file would be dependent on where/how you installed Python3. You should be able to use locate Python.h and find the Python3 directory.

Related

Torch : compiling custom op defined in a namespace gives undefined symbol

I am trying to compile a custom pytorch module with functions and classes from a namespace, and this results in a dynamic library having undefined symbol. However, if i take the namespace away, the module worked. What is wrong with the codes?
My codes looks like:
// scatter.h
#include <iostream>
#include <vector>
#include <string>
#include <stdint.h>
// namespace perception
// {
struct pShape
{
static const int32_t kMaxDimSize = 8;
int32_t dim_num_ = 0;
int32_t dim_[kMaxDimSize] = {0};
int32_t type_[kMaxDimSize] = {0};
pShape() {}
inline int32_t CalculateSize() const
{
int32_t size = 1;
for (int32_t dim = 0; dim < dim_num_; dim++)
{
size *= dim_[dim];
}
return size;
}
inline pShape(std::vector<int32_t> shape_data)
{
if (shape_data.size() > kMaxDimSize)
{
std::cerr << "Shape data size must not be larger than " << kMaxDimSize << std::endl;
exit(-1);
}
else
{
dim_num_ = shape_data.size();
for (uint32_t i = 0; i < shape_data.size(); i++)
{
dim_[i] = shape_data[i];
}
}
}
std::string ToString() const
{
std::string output = "";
if (dim_num_ > 0)
{
for (int32_t i = 0; i < dim_num_ - 1; i++)
{
output += std::to_string(dim_[i]);
output += "x";
}
output += std::to_string(dim_[dim_num_ - 1]);
}
else
{
std::cerr << "Warning, Shape's dimension number is 0. " << std::endl;
}
return output;
}
bool operator==(const pShape &shape_another) const
{
if (dim_num_ == shape_another.dim_num_)
{
for (int32_t i = 0; i < dim_num_; i++)
{
if (dim_[i] != shape_another.dim_[i])
{
return false;
}
}
return true;
}
else
{
return false;
}
}
bool operator!=(const pShape &shape_another) const
{
return !(*this == shape_another);
}
};
template <typename T_D>
int32_t LaunchGatherKernel(int32_t batch_size, const T_D *input,
T_D *output, const int32_t *index,
const int32_t dim, pShape &input_shape,
pShape &output_shape, void* stream);
// }
// scatter.cu
#include "scatter.h"
// #include "utils/atomics.h" // must be included in .cu
#include <cuda.h>
#include <cuda_runtime.h>
#include <cuda_fp16.h>
const static int kThreadPerBlock=1024;
// namespace perception
// {
template <typename T_D>
__global__ void gather_kernel(const T_D *input,
T_D *output,
const int32_t *index,
int32_t output_size,
pShape input_shape,
pShape output_shape,
int32_t dim)
{
int32_t idx = blockIdx.x * blockDim.x + threadIdx.x;
__shared__ unsigned char shapes_ptr[2 * sizeof(pShape)];
pShape *shapes = (pShape *)shapes_ptr;
if (threadIdx.x == 0)
{
shapes[0] = input_shape;
shapes[1] = output_shape;
}
__syncthreads();
if (idx >= output_size)
return;
int32_t tmp = idx;
int32_t out_idx = index[idx];
const int32_t channels = output_shape.dim_num_;
int32_t dim_index[8] = {0};
int32_t cum_prod = 1;
for (int i = channels - 1; i >= 0; --i)
{
int c_value = shapes[1].dim_[i];
cum_prod *= c_value;
int32_t index_i = tmp % c_value;
dim_index[i] = index_i;
tmp = (tmp - index_i) / c_value;
}
// this will cause diverge
if (dim == 0)
{
out_idx = out_idx * shapes[0].dim_[1] + dim_index[1];
}
else
{
out_idx = dim_index[0] * shapes[0].dim_[1] + out_idx;
}
output[idx] = input[out_idx];
}
template <typename T_D>
int32_t LaunchGatherKernel(int32_t batch_size,
const T_D *input,
T_D *output,
const int32_t *index,
const int32_t dim,
pShape &input_shape,
pShape &output_shape,
void* stream)
{
int32_t output_size = output_shape.CalculateSize();
int32_t input_size = input_shape.CalculateSize();
int32_t block_size = (output_size + kThreadPerBlock - 1) / kThreadPerBlock;
dim3 grid_dim(block_size, 1, 1);
dim3 block_dim(kThreadPerBlock, 1, 1);
// START_CUDA_PERF("gather", stream);
gather_kernel<T_D><<<grid_dim, block_dim, 0, (cudaStream_t) stream>>>(input, output, index,
output_size, input_shape, output_shape, dim);
// CUDA_CHECK_RETURN(cudaGetLastError());
// STOP_CUDA_PERF("gather");
return 0;
}
template int32_t LaunchGatherKernel<float>(int32_t batch_size,
const float *input,
float *output,
const int32_t *index,
const int32_t dim,
pShape &input_shape,
pShape &output_shape,
void* stream);
template <typename T>
__global__ void GatherSimpleKernel(T* out, T* in,
int32_t* index, int32_t lenind,
int32_t numchannel, int32_t broadcast)
{
int32_t idx = blockIdx.x * blockDim.x + threadIdx.x;
if (broadcast == 0) // no broadcast, which means the index is (lenind * numchannel)
{
out[]
return;
}
if (broadcast == 1) // broadcast index to every channel. index is (lenind)
{
return;
}
}
template <typename T>
void GatherSimpleKernelLauncher(T* out, T* in,
int32_t* index, int32_t lenind,
int32_t numchannel, int32_t broadcast,
void* stream)
#ifdef CUDA_HALF
template int32_t LaunchGatherKernel<half>(int32_t batch_size,
const half *input,
half *output,
const int32_t *index,
const int32_t dim,
Shape &input_shape,
Shape &output_shape,
void* stream);
#endif
// }
// scatter_torch.cpp
#include <torch/extension.h>
#include <torch/serialize/tensor.h>
#include "scatter.h"
#pragma common
using std::vector;
// using namespace perception;
// *** torch cpp interface *** //
void scatter_gather(torch::Tensor output_feature, torch::Tensor input_feature,
torch::Tensor index){
float * d_input_feature= input_feature.data_ptr<float>();
float * d_output_feature= output_feature.data_ptr<float>();
int32_t * d_index= index.data_ptr<int32_t>();
// Find shape for features
int dim_input = input_feature.dim();
int dim_output = output_feature.dim();
vector<int> size_input;
vector<int> size_output;
for (int i=0;i<dim_input; i++) size_input.push_back(input_feature.size(i));
for (int i=0;i<dim_output; i++) size_output.push_back(output_feature.size(i));
pShape input_shape(size_input);
pShape output_shape(size_output);
// Run kernel
LaunchGatherKernel(1, d_input_feature,
d_output_feature, d_index,
dim_input, input_shape,
output_shape, NULL);
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("scatter_gather", &scatter_gather, "scatter_gather");
}
Here is my compile script:
import os
import subprocess
from setuptools import find_packages, setup
from torch.utils.cpp_extension import BuildExtension, CUDAExtension
__COMMON_PATH = os.path.dirname(os.path.dirname(
os.path.abspath(__file__))) + '/common'
def get_git_commit_number():
if not os.path.exists('.git'):
return '0000000'
cmd_out = subprocess.run(
['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE)
git_commit_number = cmd_out.stdout.decode('utf-8')[:7]
return git_commit_number
def make_cuda_ext(name, module, sources,
include_dirs=[''], library_dirs=[''], libraries=[]):
cuda_ext = CUDAExtension(
name='%s.%s' % (module, name),
sources=[os.path.join(*module.split('.'), src) for src in sources],
include_dirs=include_dirs,
library_dirs=library_dirs,
libraries=libraries,
)
return cuda_ext
if __name__ == '__main__':
version = '0.1'
print("setup info: ")
source_files = ['core/scatter_torch.cpp', 'core/scatter.cu']
include_dirs = ['%s/include' % __COMMON_PATH]
library_dirs = ['/usr/lib']
# library_link = ['nppial', 'nppc', 'npps', 'nppif',
# 'nppig', 'nppicc', 'nppicom', 'nppidei', 'nppim']
# library_link = []
print("COMMON_PATH: %s" % __COMMON_PATH)
print("source_files: ", source_files)
print("include_dirs: ", include_dirs)
# print("link libraries: ", library_link)
print("Start building extension.\n")
setup(
name='scatter',
version=version,
description='scatter',
install_requires=[
'numpy',
'torch',
],
author='cwman',
author_email='qzh19950601#gmail.com',
license='Apache License 2.0',
packages=find_packages(),
cmdclass={'build_ext': BuildExtension},
ext_modules=[
make_cuda_ext(
name='scatter',
module='.',
sources=source_files,
include_dirs=include_dirs,
library_dirs=library_dirs,
# libraries=library_link
),
],
)
And if the scatter.*.so is imported, the python gives undefined symbol error when undoing the namespace comment.

Linking error when compiling code using cython

I have the following c++ and header file, which I would like to compile in Cython:
c++ code:
\\ CA.cpp c++ code
#include "CA.h"
#include<iostream>
#include <vector>
Cell::Cell() {}
// construct cell: cell is defined with position (x,y,z coordinates) and neighborhood
Cell::Cell(int x, int y, int z) {
this->x = x;
this->y = y;
this->z = z;
std::vector<int> ngh;
}
// Return the neighbors of the cell
void Cell::neighbors( ) {
std::vector<int> a{ -1, 0, 1 };
std::vector<int> ranges(gridSize, 0);
for (int i = 0; i < ranges.size(); i++) {
ranges[i] = i;
}
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
for (int k = -1; k <= 1; k++) {
int x_ngh = ranges[Cell::x + i];
int y_ngh = ranges[Cell::y + j];
int z_ngh = ranges[Cell::z + k];
int id_ngh = x_ngh + (y_ngh - 1) * gridSize + (z_ngh - 1) * gridSize * gridSize;
Cell:ngh.push_back(id_ngh);
}
}
}
}
and header file:
\\CA.h
#ifndef CA_H
#define CA_H
#include <vector>
const int gridSize = 400;
class Cell {
public:
int x, y, z;
std::vector<int> ngh;
Cell();
Cell(int x, int y, int z);
void neighbors();
};
#endif
In order to compile the code using cython I created CA.pxd file:
cdef extern from "CA.cpp":
pass
# Declare the class with cdef
cdef extern from "CA.h":
cdef cppclass Cell:
Cell() except +
Cell(int, int, int) except +
int x,y,z
neighbors()
and CA.pyx
# distutils: language = c++
from CA cimport *
from libcpp.vector cimport vector
cdef class PyCell:
cdef Cell c_cell
def __cinit__(self, int x, int y, int z):
self.c_cell = Cell(x,y,z)
def neighbors(self):
return self.c_cell.neighbors()
Finally, I created setup.py file:
from Cython.Build import cythonize
setup(ext_modules=cythonize("CA.pyx"))
When I run the command:
python setup.py build_ext --inplace
I get an error LINK : error LNK2001: unresolved external symbol PyInit_CA. I don't know why. Any help greatly appreciated.

Call c++ functions from Python in anaconda windows

I've tried to call some c++ functions from Python. I am using python from windows in anaconda.
I tried it with types, but it appears an error when loading the shared library (both .dll and .so)
DO you know any simple way to call these functions from Python?
Here you have the c++ functions:
#include <vector>
#include <cmath>
#include <ctime>
#include <stdio.h>
#include <string>
using namespace std;
typedef vector<double> VE;
typedef vector<VE> VVE;
double sq_norm(VE x, VE y, double s_v, double hat_s_v) {
double sq_norm = 0;
int size = x.size();
for (int i = 0; i < size; ++i){
sq_norm += pow((hat_s_v/s_v)*x[i]-y[i],2);
}
return sq_norm;
}
VE gradient_rho(VE y, VVE eta, double s_v, double hat_s_v){
int nu = eta[0].size();
int N = eta.size();
VE gradient(nu,0);
for (int j = 0; j < N; ++j) {
VVE add = VVE(nu, VE(1, 0));
double exponential = exp(-0.5*sq_norm(eta[j], y, s_v, hat_s_v)/(pow(hat_s_v,2)));
for (int i = 0; i < nu; ++i) {
gradient[i] += ((hat_s_v/s_v)*eta[j][i]-y[i])*exponential/(N*(pow(hat_s_v,2)));
}
}
return gradient;
}
double rho(VE y, VVE eta, double s_v, double hat_s_v){
int nu = eta[0].size();
int N = eta.size();
double rho_ = 0;
for (int j = 0; j < N; ++j) {
VVE add = VVE(nu, VE(1, 0));
double exponential = exp(-0.5*sq_norm(eta[j], y, s_v, hat_s_v)/(pow(hat_s_v,2)));
rho_ += exponential/N;
}
return rho_;
}
The command to generate the .ddl file is:
cpp -fPIC -shared -o library.dll library.cc
The python script where I try to load the library is:
from ctypes import *
import ctypes
import pathlib
so_file = "C://Users//asus2//Box Sync//Box Sync//PLOM With Constraints//Code//library.dll"
c_lib = ctypes.windll.LoadLibrary(so_file)

"undefined symbol" error when importing SWIG+python module

I created a *.so file for use in Python using SWIG, but when I import it I get this:
/_analyzer.so: undefined symbol: autocorellation
I did almost everything according to this instruction: https://scipy.github.io/old-wiki/pages/Cookbook/SWIG_NumPy_examples.html
my code is following:
analyzer.h:
void autocorellation(double *in, double *out, long long n);
analyzer.cpp:
#include "analyzer.h"
#include <math.h>
#include <stdlib.h>
#define PI 3.14159265358979323846
typedef struct {
double real;
double im;
} Complex;
void complex_multiply(Complex a,Complex b,Complex* c){
c->real = a.real * b.real - a.im * b.im;
c->im = a.real * b.im + a.im * b.real;
}
void complex_multiply_int(int a, Complex b,Complex* c){
c->real = a * b.real;
c->im = a * b.im;
}
void complex_sum(Complex a,Complex b,Complex* c){
c->real = a.real + b.real;
c->im = a.im + b.im;
}
void complex_conjugate(Complex* a,Complex* b,long long n){
for(int i = 0; i < n; ++i){
b[i].real = a[i].real;
b[i].im = -1 * a[i].im;
}
}
long long rev (long long num, long long lg_n) {
long long res = 0;
for (long long i=0; i < lg_n; ++i)
if (num & (1 << i))
res |= 1 << (lg_n-1-i);
return res;
}
void fft (Complex* a, long long n,bool invert) {
long long lg_n = 0;
while ((1 << lg_n) < n)
++lg_n;
for (long long i=0; i<n; ++i){
long long r= rev(i,lg_n);
if (i < r){
a[i].real = a[i].real + a[r].real;
a[r].real = a[i].real - a[r].real;
a[i].real = a[i].real - a[r].real;
a[i].im = a[i].im + a[r].im;
a[r].im = a[i].im - a[r].im;
a[i].im = a[i].im - a[r].im;
}
}
for (long long len=2; len<=n; len <<= 1) {
double ang = 2*PI/len * (invert ? -1 : 1);
Complex wn;
wn.real = cos(ang);
wn.im = sin(ang);
for (long long i=0; i<n; i+=len) {
Complex w;
w.real = 1;
w.im = 0;
long long ll = (long long)(len * 0.5);
for (long long j=0; j< ll; ++j) {
Complex u = a[i+j],v;
complex_multiply(a[i+j+ll],w,&v);
complex_sum(u,v,&a[i+j]);
complex_multiply_int(-1,v,&v);
complex_sum(u,v,&a[i+j+ll]);
complex_multiply(w,wn,&w);
}
}
}
if (invert)
for (long long i=0; i<n; ++i){
a[i].real /= n;
a[i].im /= n;
}
}
void autocorellation(double *in, double *out, long long n){
long long le = 1;
while(n > le)
le *= 2;
double m = 0;
for(int i = 0; i < n; ++i)
m+=in[i];
m /= n;
for(int i = 0; i < n; ++i)
in[i] -= m;
Complex* a = (Complex*) malloc(le*sizeof(Complex));
Complex* b = (Complex*) malloc(le*sizeof(Complex));
for(long long i = 0; i < n; ++i){
a[i].im = 0;
a[i].real = in[i];
}
for(long long i = n; i < le; ++i){
a[i].im = 0;
a[i].real = 0;
}
fft(a,le,false);
complex_conjugate(a,b,le);
Complex* c = (Complex*) malloc(le*sizeof(Complex));
for(long long i = 0; i < le; ++i)
complex_multiply(b[i],a[i],&c[i]);
fft(c,le,true);
for(long long i = 0; i < n; ++i)
out[i] = (c[i].real/c[0].real);
free(a);
free(b);
free(c);
}
analyzer.i:
%module analyzer
%{
#define SWIG_FILE_WITH_INIT
#include "analyzer.h"
%}
%include "numpy.i"
%init %{
import_array();
%}
%apply (double* IN_ARRAY1,int DIM1) {(double *in, long long n)}
%apply (double* ARGOUT_ARRAY1,int DIM1) {(double *out, long long n)}
%include "analyzer.h"
setup.py:
#! /usr/bin/env python
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# ezrange extension module
_analyzer = Extension("_analyzer",
["analyzer.i","analyzer.cpp"],
include_dirs = [numpy_include],
)
# ezrange setup
setup( name = "range function",
description = "Autocorellation function evaluation",
author = "Bodya",
version = "1.0",
ext_modules = [_analyzer]
)
The difference between your code and the cookbook examples is that your code is C++. Therefore, you need to pass the -c++ option to SWIG. In the construction of Extension(...) in setup.py, simply add swig_opts=['-c++'],.
Note that distutils will still invoke the C compiler on the generated wrapper file, but this will have a .cpp extension, so it should be compiled correctly if the compiler is gcc or clang.
My experience using distutils or setuptools for C++ SWIG extensions that are even slightly beyond trivial has been poor, so I invoke SWIG to generate a wrapper outside of distutils (I do it via a Makefile) and only use distutils to compile the extension from the wrapper file.

Pass a 2d numpy array to c using ctypes

What is the correct way to pass a numpy 2d - array to a c function using ctypes ?
My current approach so far (leads to a segfault):
C code :
void test(double **in_array, int N) {
int i, j;
for(i = 0; i<N; i++) {
for(j = 0; j<N; j++) {
printf("%e \t", in_array[i][j]);
}
printf("\n");
}
}
Python code:
from ctypes import *
import numpy.ctypeslib as npct
array_2d_double = npct.ndpointer(dtype=np.double,ndim=2, flags='CONTIGUOUS')
liblr = npct.load_library('libtest.so', './src')
liblr.test.restype = None
liblr.test.argtypes = [array_2d_double, c_int]
x = np.arange(100).reshape((10,10)).astype(np.double)
liblr.test(x, 10)
This is probably a late answer, but I finally got it working. All credit goes to Sturla Molden at this link.
The key is, note that double** is an array of type np.uintp. Therefore, we have
xpp = (x.ctypes.data + np.arange(x.shape[0]) * x.strides[0]).astype(np.uintp)
doublepp = np.ctypeslib.ndpointer(dtype=np.uintp)
And then use doublepp as the type, pass xpp in. See full code attached.
The C code:
// dummy.c
#include <stdlib.h>
__declspec(dllexport) void foobar(const int m, const int n, const
double **x, double **y)
{
size_t i, j;
for(i=0; i<m; i++)
for(j=0; j<n; j++)
y[i][j] = x[i][j];
}
The Python code:
# test.py
import numpy as np
from numpy.ctypeslib import ndpointer
import ctypes
_doublepp = ndpointer(dtype=np.uintp, ndim=1, flags='C')
_dll = ctypes.CDLL('dummy.dll')
_foobar = _dll.foobar
_foobar.argtypes = [ctypes.c_int, ctypes.c_int, _doublepp, _doublepp]
_foobar.restype = None
def foobar(x):
y = np.zeros_like(x)
xpp = (x.__array_interface__['data'][0]
+ np.arange(x.shape[0])*x.strides[0]).astype(np.uintp)
ypp = (y.__array_interface__['data'][0]
+ np.arange(y.shape[0])*y.strides[0]).astype(np.uintp)
m = ctypes.c_int(x.shape[0])
n = ctypes.c_int(x.shape[1])
_foobar(m, n, xpp, ypp)
return y
if __name__ == '__main__':
x = np.arange(9.).reshape((3, 3))
y = foobar(x)
Hope it helps,
Shawn
#include <stdio.h>
void test(double (*in_array)[3], int N){
int i, j;
for(i = 0; i < N; i++){
for(j = 0; j < N; j++){
printf("%e \t", in_array[i][j]);
}
printf("\n");
}
}
int main(void)
{
double a[][3] = {
{1., 2., 3.},
{4., 5., 6.},
{7., 8., 9.},
};
test(a, 3);
return 0;
}
if you want to use a double ** in your function, you must pass an array of pointer to double (not a 2d array):
#include <stdio.h>
void test(double **in_array, int N){
int i, j;
for(i = 0; i < N; i++){
for(j = 0; j< N; j++){
printf("%e \t", in_array[i][j]);
}
printf("\n");
}
}
int main(void)
{
double a[][3] = {
{1., 2., 3.},
{4., 5., 6.},
{7., 8., 9.},
};
double *p[] = {a[0], a[1], a[2]};
test(p, 3);
return 0;
}
Another (as suggested by #eryksun): pass a single pointer and do some arithmetic to get the index:
#include <stdio.h>
void test(double *in_array, int N){
int i, j;
for(i = 0; i < N; i++){
for(j = 0; j< N; j++){
printf("%e \t", in_array[i * N + j]);
}
printf("\n");
}
}
int main(void)
{
double a[][3] = {
{1., 2., 3.},
{4., 5., 6.},
{7., 8., 9.},
};
test(a[0], 3);
return 0;
}
While the reply might be rather late, I hope it could help other people with the same problem.
As numpy arrays are internally saved as 1d arrays, one can simply rebuild 2d shape in C. Here is a small MWE:
// libtest2d.c
#include <stdlib.h> // for malloc and free
#include <stdio.h> // for printf
// create a 2d array from the 1d one
double ** convert2d(unsigned long len1, unsigned long len2, double * arr) {
double ** ret_arr;
// allocate the additional memory for the additional pointers
ret_arr = (double **)malloc(sizeof(double*)*len1);
// set the pointers to the correct address within the array
for (int i = 0; i < len1; i++) {
ret_arr[i] = &arr[i*len2];
}
// return the 2d-array
return ret_arr;
}
// print the 2d array
void print_2d_list(unsigned long len1,
unsigned long len2,
double * list) {
// call the 1d-to-2d-conversion function
double ** list2d = convert2d(len1, len2, list);
// print the array just to show it works
for (unsigned long index1 = 0; index1 < len1; index1++) {
for (unsigned long index2 = 0; index2 < len2; index2++) {
printf("%1.1f ", list2d[index1][index2]);
}
printf("\n");
}
// free the pointers (only)
free(list2d);
}
and
# test2d.py
import ctypes as ct
import numpy as np
libtest2d = ct.cdll.LoadLibrary("./libtest2d.so")
libtest2d.print_2d_list.argtypes = (ct.c_ulong, ct.c_ulong,
np.ctypeslib.ndpointer(dtype=np.float64,
ndim=2,
flags='C_CONTIGUOUS'
)
)
libtest2d.print_2d_list.restype = None
arr2d = np.meshgrid(np.linspace(0, 1, 6), np.linspace(0, 1, 11))[0]
libtest2d.print_2d_list(arr2d.shape[0], arr2d.shape[1], arr2d)
If you compile the code with gcc -shared -fPIC libtest2d.c -o libtest2d.so and then run python test2d.py it should print the array.
I hope the example is more or less self-explaining. The idea is, that the shape is also given to the C-Code which then creates a double ** pointer for which the space for the additional pointers is reserved. And these then are then set to point to the correct part of the original array.
PS: I am rather a beginner in C so please comment if there are reasons not to do this.
Here i hava pass two 2d numpy array and print value of one array for the reference
you can use and write your own logic in cpp
cpp_function.cpp
compile it using : g++ -shared -fPIC cpp_function.cpp -o cpp_function.so
#include <iostream>
extern "C" {
void mult_matrix(double *a1, double *a2, size_t a1_h, size_t a1_w,
size_t a2_h, size_t a2_w, int size)
{
//std::cout << "a1_h & a1_w" << a1_h << a1_w << std::endl;
//std::cout << "a2_h & a2_w" << a2_h << a2_w << std::endl;
for (size_t i = 0; i < a1_h; i++) {
for (size_t j = 0; j < a1_w; j++) {
printf("%f ", a1[i * a1_h + j]);
}
printf("\n");
}
printf("\n");
}
}
Python File
main.py
import ctypes
import numpy
from time import time
libmatmult = ctypes.CDLL("./cpp_function.so")
ND_POINTER_1 = numpy.ctypeslib.ndpointer(dtype=numpy.float64,
ndim=2,
flags="C")
ND_POINTER_2 = numpy.ctypeslib.ndpointer(dtype=numpy.float64,
ndim=2,
flags="C")
libmatmult.mult_matrix.argtypes = [ND_POINTER_1, ND_POINTER_2, ctypes.c_size_t, ctypes.c_size_t]
# print("-->", ctypes.c_size_t)
def mult_matrix_cpp(a,b):
shape = a.shape[0] * a.shape[1]
libmatmult.mult_matrix.restype = None
libmatmult.mult_matrix(a, b, *a.shape, *b.shape , a.shape[0] * a.shape[1])
size_a = (300,300)
size_b = size_a
a = numpy.random.uniform(low=1, high=255, size=size_a)
b = numpy.random.uniform(low=1, high=255, size=size_b)
t2 = time()
out_cpp = mult_matrix_cpp(a,b)
print("cpp time taken:{:.2f} ms".format((time() - t2) * 1000))
out_cpp = numpy.array(out_cpp).reshape(size_a[0], size_a[1])

Categories

Resources