Complex number in ctypes

Question:

This might be a bit foolish but I’m trying to use ctypes to call a function that receives a complex vector as a paramter. But in ctypes there isn’t a class c_complex.
Does anybody know how to solve this?

edit: I’m refering to python’s ctypes, in case there are others….

Asked By: xarles

||

Answers:

If c_complex is a C struct and you have the definition of in documentation or a header file you could utilize ctypes to compose a compatible type. It is also possible, although less likely, that c_complex is a typdef for a type that ctypes already supports.

More information would be needed to provide a better answer…

Answered By: Josh Heitzman

I think that you mean the C99 complex types, e.g. “_Complex double”. These types are not natively supported by ctypes. See for example the discussion there: https://bugs.python.org/issue16899

Answered By: Armin Rigo

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

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 
Answered By: user3903270

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

Let’s say you have a C/C++/Fortran procedure that takes a scalar complex argument. Then you would need to write a wrapper procedure that takes two floats / doubles (the 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 let’s keep it simple with a single complex number for now.

C

For example, let’s say 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 + %.1fin", creal(z), cimag(z));
}

Then we would have to add a wrapper function (at the end of the same file) like this:

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

Compile that into a 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 fiddly because an extern "C" interface needs to be defined to avoid name-mangling and we need to deal with the class in Python (both as per this SO Q&A).

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

#include <stdio.h>
#include <complex>

class ComplexPrinter
{
    public:
        void printComplex(std::complex<double> z)
        {   
            printf("%.1f + %.1fin", 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 an extern "C" interface (at the end of the same file) like this:

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);
    }   
}

Compile into a 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 subroutine 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 the 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 into a library in the usual way:

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

And 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))
Answered By: Biggsy

I don’t really like the @Biggsy answer above, because it requires additional wrapper written in c/fortran. For a simple case it does not matter, but if you want to use external libraries (like lapack) that would require you to make some proxy dll for every function/library you want to call and it probably won’t be fun and many things can go wrong (like linkage, reusability e.t.c). My approach relies only on python with the aid of ctypes and numpy. Hope this will be helpful.

In the example below I show how to pass complex numbers/arrays between python, numpy (both are essentially C) and pure C or fortran (i use fortran, but for C it is exactly the same on python side).

First lets make a dummy fortran (f90) library, say f.f90, with C interface:

! print complex number
subroutine print_c(c) bind(c)
  use iso_c_binding
  implicit none
  complex(c_double_complex), intent(in) :: c
  print "(A, 2F20.16)", "@print_c, c=", c
end subroutine print_c

! multiply real numbers
real(c_double) function mtp_rr(r1,r2) bind(c)
  use iso_c_binding
  implicit none
  real(c_double), intent(in) :: r1,r2
  print "(A)", "@mpt_rr" ! make sure its fortran
  mtp_rr = r1 * r2
end function mtp_rr

! multiply complex numbers
complex(c_double_complex) function mtp_cc(c1,c2) bind(c)
  use iso_c_binding
  implicit none
  complex(c_double_complex), intent(in) :: c1,c2
  print "(A)", "@mpt_cc" ! make sure its fortran
  mtp_cc = c1 * c2
  return
end function mtp_cc

! print array of complex numbers
subroutine print_carr(n, carr) bind(c)
  use iso_c_binding
  implicit none
  integer(c_long), intent(in) :: n
  integer(c_long) :: i
  complex(c_double_complex), intent(in) :: carr(n)
  print "(A)", "@print_carr"
  do i=1,n
    print "(I5,2F20.16)", i, carr(i)
  end do
end subroutine print_carr

The library can be simply compiled as usual (in this example into libf.so). Notice that each subroutine contains its own "use" statement, this can be avoided if declare "use iso_c_binding" on module level. (I don’t know why gfortran understands type of functions without global use of iso_c_binding, but it works and its fine for me.)

gfortran -shared -fPIC f.f90 -o libf.so

Then i created a python script, say p.py, with the following content:

#!/usr/bin/env python3
import ctypes as ct
import numpy as np
## ctypes 1.1.0
## numpy 1.19.5
# print("ctypes", ct.__version__)
# print("numpy", np.__version__)
from numpy.ctypeslib import ndpointer
## first we prepare some datatypes
c_double = ct.c_double
class c_double_complex(ct.Structure): 
    """complex is a c structure
    https://docs.python.org/3/library/ctypes.html#module-ctypes suggests
    to use ctypes.Structure to pass structures (and, therefore, complex)
    """
    _fields_ = [("real", ct.c_double),("imag", ct.c_double)]
    @property
    def value(self):
        return self.real+1j*self.imag # fields declared above
c_double_complex_p = ct.POINTER(c_double_complex) # pointer to our complex
## pointers
c_long_p = ct.POINTER(ct.c_long) # pointer to long (python `int`)
c_double_p = ct.POINTER(ct.c_double) # similar to ctypes.c_char_p, i guess?
# ndpointers work well with unknown dimension and shape
c_double_complex_ndp = ndpointer(np.complex128, ndim=None)
### ct.c_double_complex
### > AttributeError: module 'ctypes' has no attribute 'c_double_complex'

## then we prepare some dummy functions to simplify argument passing
b_long = lambda i: ct.byref(ct.c_long(i))
b_double = lambda d: ct.byref(ct.c_double(d))
b_double_complex = lambda c: ct.byref(c_double_complex(c.real, c.imag))

## now we load the library
libf = ct.CDLL("libf.so")

## and define IO types of its functions/routines
print_c = libf.print_c
print_c.argtypes = [c_double_complex_p] # fortran accepes only references
print_c.restype = None # fortran subroutine (c void)

mtp_rr = libf.mtp_rr
mtp_rr.argtypes = [c_double_p, c_double_p] 
mtp_rr.restype = c_double # ctypes.c_double

mtp_cc = libf.mtp_cc
mtp_cc.argtypes = [c_double_complex_p, c_double_complex_p] 
mtp_cc.restype = c_double_complex # custom c_double_complex

print_carr = libf.print_carr
print_carr.argtypes = [c_long_p, c_double_complex_ndp]
print_carr.restype = None

## finally we can prepare some data and test what we got
print("test print_c")
c = 5.99j+3.1234567890123456789
print_c(b_double_complex(c)) # directly call c/fortran function
print(c)

print("ntest mtp_rr")
d1 = 2.2
d2 = 3.3
res_d = mtp_rr(b_double(d1),b_double(d2))
print("d1*d2 =", res_d)

print("ntest mtp_cc")
c1 = 2j+3
c2 = 3j
res = mtp_cc(b_double_complex(c1), b_double_complex(c2))
print(res.value)

print("ntest print_carr")
n = 10
arr = np.arange(n) + 10j * np.arange(10)
print("arr.shape =",arr.shape)
print_carr(b_long(n), arr)
arr = arr.reshape((5,2)) # still contiguous chunk of memory
print("arr.shape =",arr.shape)
print_carr(b_long(n), arr) # same result
print("done")

and everything works. I have no idea why something like this is not implemented yet in ctypes.

Also related to other suggestions here: you can pass complex numbers by making new ctypes type

__c_double_complex_p = ct.POINTER(2*ct.c_double)
dll.some_function.argtypes = [__c_double_complex_p, ...]

but you can not retrieve results in that way! To set proper restype only method via class worked for me.

Answered By: Ivan

My problem was to pass a numpy array of complex values from Python to C++.
The steps followed where:

  1. Creating a numpy array in Python as

    data=numpy.ascontiguousarray(data,dtype=complex)

  2. Creating a double pointer to the data.

    c_double_p = ctypes.POINTER(ctypes.c_double)
    data_p = data.ctypes.data_as(c_double_p)

  3. Obtaining the value in C++ by defining the parameter as

extern "C" void sample(std::complex < double > *data_p)

Answered By: arnold jojo
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.