0

I'm trying to run the following C function via Python and get the segmentation error.

void WKMV(
    const double *const time2,
    const int *const status,
    const double *const weights,
    const int *const delta,
    const int *const len,
    const int *const end,
    double *const surv)
{
    register int i;
    double n, d;
    *surv = 1;
    for (i = *len-1, n = 0; i >= *end; i--) { // loop in reverse order until end index is reached
        n += delta[i]*weights[i]; // initialize living weighting
    }
    while (i >= 0) { // loop through the sample in reverse order until zero index is reached
        n += delta[i]*weights[i];
        d = status[i]*weights[i]; // initialize dead weighting
        for (i--; i >= 0 && time2[i] == time2[i+1]; i--) { // loop in reverse order until time changes or zero index is reached
            n += delta[i]*weights[i]; // weight the living
            d += status[i]*weights[i]; // weight the dead
        }
        if (n > 0) *surv *= 1-d/n; // compute survival probability
    }
    return;

I am using Python Ctypes package like so. For the ease of play I have added a hardcoded data for each argument.

from ctypes import *

c_funcs = CDLL('filepath/file.so')
WKMV = c_funcs.WKMV

def do_WKMV_using_c():


    """Call C function"""

    n = 10

    # Declaring the variables
    time2 = [58.72, 41.9, 16.23, 145.44, 10.56, 54.95, 196.46, 194.03, 20.95, 20.0]
    status = [1, 1, 0, 0, 0, 0, 0, 0, 1, 0]
    time1 = [6.36, 4.91, 6.53, 4.77, 5.59, 6.9, 3.05, 6.17, 5.19, 6.41]
    delta = [1]*n
    weights = [0.5]*n
    surv = 1.0


    #Converting variables to format readable to C
    c_arr_time2 = (c_double * n)(*time2)
    c_arr_status = (c_int * n)(*status)
    c_arr_weights = (c_double * n)(*weights)
    c_arr_delta = (c_int * n)(*delta)
    c_int_len = c_int(n)
    c_float_surv = c_double(surv)
    c_int_end = c_int(n)

    WeightedKaplanMeierValue.restype = None
    WeightedKaplanMeierValue(c_arr_time2,c_arr_status, c_arr_weights, c_arr_delta,
                             c_int_len, c_int_end, c_float_surv)

    c_res_out = c_float_surv

    return c_res_out

print(do_WKMV_using_c())

I get the following error

Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

From the looks of it I have translated all of the arguments into readable C code. Also as I can see(I am a noob at C) that there are no internal functions within the C function. So not sure where the error is. Also is there are way to get more detailed error message from C? Any help on that?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847

1 Answers1

1

The error is not setting .argtypes so ctypes can check that parameters are passed correctly. If set, the Python code would error indicating some parameters weren't correct. Here's the fix:

test.py - commented changed code

from ctypes import *

c_funcs = CDLL('./test')
WeightedKaplanMeierValue = c_funcs.WKMV
# added .argtypes
WeightedKaplanMeierValue.argtypes = (POINTER(c_double), POINTER(c_int), POINTER(c_double),
                                     POINTER(c_int), POINTER(c_int), POINTER(c_int), POINTER(c_double))
WeightedKaplanMeierValue.restype = None

def do_WKMV_using_c():
    n = 10
    time2 = [58.72, 41.9, 16.23, 145.44, 10.56, 54.95, 196.46, 194.03, 20.95, 20.0]
    status = [1, 1, 0, 0, 0, 0, 0, 0, 1, 0]
    time1 = [6.36, 4.91, 6.53, 4.77, 5.59, 6.9, 3.05, 6.17, 5.19, 6.41]
    delta = [1]*n
    weights = [0.5]*n
    surv = 1.0

    c_arr_time2 = (c_double * n)(*time2)
    c_arr_status = (c_int * n)(*status)
    c_arr_weights = (c_double * n)(*weights)
    c_arr_delta = (c_int * n)(*delta)
    c_int_len = c_int(n)
    c_float_surv = c_double(surv)
    c_int_end = c_int(n)

# Pass the address of the last three scalar values.
WeightedKaplanMeierValue(c_arr_time2, c_arr_status, c_arr_weights, c_arr_delta,
                             byref(c_int_len), byref(c_int_end), byref(c_float_surv))

    # Add .value to extract the Python object.
    return c_float_surv.value

print(do_WKMV_using_c())

test.c - DLL used to test. No changes to OP code except to export the function on Windows.

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

API void WKMV(
    const double *const time2,
    const int *const status,
    const double *const weights,
    const int *const delta,
    const int *const len,
    const int *const end,
    double *const surv)
{
    register int i;
    double n, d;
    *surv = 1;
    for (i = *len-1, n = 0; i >= *end; i--) { // loop in reverse order until end index is reached
        n += delta[i]*weights[i]; // initialize living weighting
    }
    while (i >= 0) { // loop through the sample in reverse order until zero index is reached
        n += delta[i]*weights[i];
        d = status[i]*weights[i]; // initialize dead weighting
        for (i--; i >= 0 && time2[i] == time2[i+1]; i--) { // loop in reverse order until time changes or zero index is reached
            n += delta[i]*weights[i]; // weight the living
            d += status[i]*weights[i]; // weight the dead
        }
        if (n > 0) *surv *= 1-d/n; // compute survival probability
    }
    return;
}

Output:

0.39999999999999997
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Brilliant thanks! That worked. Sorry for the late reply. It works without the byref() for me as well. Can you explain why do I need to use it? – Yury Moskaltsov Jun 20 '22 at 16:28
  • @YuryMoskaltsov Because `.argtypes` is set, `ctypes` knows for example that if you pass a `c_int()` and `.argtypes` says the parameter should be a `POINTER(c_int)` type that it can do the `byref` for you. It's a good habit to *always* set `.argtypes` and `.restype` so `ctypes` can do these checks for you. – Mark Tolonen Jun 20 '22 at 19:49