Complex number in ctypes

It might be a little silly, but I'm trying to use ctypes to call a function that receives a complex vector as a parameter. But in ctypes there is no c_complex class. Does anyone know how to solve this?

edit: I refer to python types if there are others ...

+4
source share
4 answers

If c_complex is a C structure and you have a definition in the documentation or header file, you can use ctypes to create a compatible type. It is also possible, although less likely, that c_complex is a typdef for a type that already supports ctypes.

To get a better answer, you need more information ...

-1
source

I think you mean the complex types of C99, for example. "_Complex double". These types are not supported by ctypes. See for example the discussion there .

-1
source

Use c_double or c_float twice, once for real and once for imaginary. For instance:

from ctypes import c_double, c_int dimType = c_int * 1 m = dimType(2) n = dimType(10) complexArrayType = (2 * c_double) * ( n * m ) complexArray = complexArrayType() status = yourLib.libcall(m,n,complexArray) 

on the library side (fortran example):

 subroutine libcall(m,n,complexArrayF) bind(c, name='libcall') use iso_c_binding, only: c_double_complex,c_int implicit none integer(c_int) :: m, n complex(c_double_complex), dimension(m,n) :: complexArrayF integer :: ii, jj do ii = 1,m do jj = 1,n complexArrayF(ii,jj) = (1.0, 0.0) enddo enddo end subroutine 
-1
source

As the OP noted in his comment on @Armin Rigo's answer, the correct way to do this is to use wrapper functions. Also, as noted in the comments (by me) to the original question, this is also possible for C ++ and Fortran. However, the way to get this to work in all three languages ​​is not necessarily obvious. Therefore, this answer is a working example for each language.

Suppose you have a C / C ++ / Fortran procedure that takes a scalar complex argument. Then you need to write a wrapper procedure that takes two floating point / double numbers (real and imaginary parts), combines them into a complex number and then calls the original procedure. Obviously, this can be extended to arrays of complex numbers, but for now, let's be simpler with one complex number.

C

For example, suppose you have a C function to print a formatted complex number. So we have my_complex.c :

 #include <stdio.h> #include <complex.h> void print_complex(double complex z) { printf("%.1f + %.1fi\n", creal(z), cimag(z)); } 

Then we need to add a wrapper function (at the end of the same file), for example, like this:

 void print_complex_wrapper(double z_real, double z_imag) { double complex z = z_real + z_imag * I; print_complex(z); } 

Compile this into the library in the usual way:

 gcc -shared -fPIC -o my_complex_c.so my_complex.c 

Then call the wrapper function from Python:

 from ctypes import CDLL, c_double c = CDLL('./my_complex_c.so') c.print_complex_wrapper.argtypes = [c_double, c_double] z = complex(1.0 + 1j * 2.0) c.print_complex_wrapper(c_double(z.real), c_double(z.imag)) 

C ++

The same thing in C ++ is a bit more complicated because you need to define an extern "C" interface to avoid name distortion, and we need to deal with a class in Python (both according to this SO Q & A ).

So now we have my_complex.cpp (to which I have already added a wrapper function):

 #include <stdio.h> #include <complex> class ComplexPrinter { public: void printComplex(std::complex<double> z) { printf("%.1f + %.1fi\n", real(z), imag(z)); } void printComplexWrapper(double z_real, double z_imag) { std::complex<double> z(z_real, z_imag); printComplex(z); } }; 

We also need to add the extern "C" interface (at the end of the same file) as follows:

 extern "C" { ComplexPrinter* ComplexPrinter_new() { return new ComplexPrinter(); } void ComplexPrinter_printComplexWrapper(ComplexPrinter* printer, double z_real, double z_imag) { printer->printComplexWrapper(z_real, z_imag); } } 

Assemble the library in the usual way:

 g++ -shared -fPIC -o my_complex_cpp.so my_complex.cpp 

And call the wrapper from Python:

 from ctypes import CDLL, c_double cpp = CDLL('./my_complex_cpp.so') cpp.ComplexPrinter_printComplexWrapper.argtypes = [c_double, c_double] class ComplexPrinter: def __init__(self): self.obj = cpp.ComplexPrinter_new() def printComplex(self, z): cpp.ComplexPrinter_printComplexWrapper(c_double(z.real), c_double(z.imag)) printer = ComplexPrinter() z = complex(1.0 + 1j * 2.0) printer.printComplex(z) 

Fortran

And finally, in Fortran, we have the original routine in my_complex.f90 :

 subroutine print_complex(z) use iso_c_binding, only: c_double_complex implicit none complex(c_double_complex), intent(in) :: z character(len=16) :: my_format = "(f4.1,a3,f4.1,a)" print my_format, real(z), " + ", aimag(z), "i" end subroutine print_complex 

To which we add a wrapper function (at the end of the same file):

 subroutine print_complex_wrapper(z_real, z_imag) bind(c, name="print_complex_wrapper") use iso_c_binding, only: c_double, c_double_complex implicit none real(c_double), intent(in) :: z_real, z_imag complex(c_double_complex) :: z z = cmplx(z_real, z_imag) call print_complex(z) end subroutine print_complex_wrapper 

Then compile to the library in the usual way:

 gfortran -shared -fPIC -o my_complex_f90.so my_complex.f90 

And a call from Python (note the use of POINTER):

 from ctypes import CDLL, c_double, POINTER f90 = CDLL('./my_complex_f90.so') f90.print_complex_wrapper.argtypes = [POINTER(c_double), POINTER(c_double)] z = complex(1.0 + 1j * 2.0) f90.print_complex_wrapper(c_double(z.real), c_double(z.imag)) 
-1
source

Source: https://habr.com/ru/post/1445922/


All Articles