2016-04-20 4 views
0

Я работаю над кодом фрактального генератора. Основной код написан на python, а часть итераций написана в fortran. Я использую f2py для склеивания двух кодов вместе. Вот Фортран функция я использую:segfault на f2py callback

function iterate(z0, func, zmax, niter) result(n) 

    implicit none 

    complex(kind=8), intent(in) :: z0 
    real(kind=8), intent(in) :: zmax 
    integer, intent(in) :: niter 
    external func 
    complex(kind=8) :: func 

    integer :: n 
    complex(kind=8) :: z 

    n = 0 
    z = z0 
    do while ((n < niter) .and. (abs(z) < zmax)) 
     z = func(z) 
     n = n + 1 
    end do 

end function iterate 

Вот строка документации для сгенерированного кода упаковщика:

n = iterate(z0,func,zmax,niter,[func_extra_args]) 

Wrapper for ``iterate``. 

Parameters 
---------- 
z0 : input complex 
func : call-back function 
zmax : input float 
niter : input int 

Other Parameters 
---------------- 
func_extra_args : input tuple, optional 
    Default:() 

Returns 
------- 
n : int 

Notes 
----- 
Call-back functions:: 

    def func(z): return z 
    Required arguments: 
    z : input complex 
    Return objects: 
    z : complex 

Я получаю Segmentation fault ошибку при попытке использования iterate с любой функцией питона обратного вызова. Вот пример результата, который я получаю:

>>> from foo import iterate 
>>> iterate(1.0j, lambda x: 4.0 + x**2, 4.0, 256) 
Segmentation fault 

Я просмотрел всю имеющуюся документацию на обратные вызовы в f2py но не нашли решение этой проблемы. Любая помощь будет оценена по достоинству.

UPDATE

Вот трассировка от БГДА:

Program received signal SIGSEGV, Segmentation fault. 
cb_func_in_iterate2__user__routines (return_value=0x7fffffffdbc0, z_cb_capi=0x3ff0000000000000) 
    at /tmp/tmpT8xG1q/src.linux-x86_64-2.7/juliamodule.c:470 
470 /tmp/tmpT8xG1q/src.linux-x86_64-2.7/juliamodule.c: No such file or directory. 
(gdb) backtrace 
#0 cb_func_in_iterate2__user__routines (return_value=0x7fffffffdbc0, z_cb_capi=0x3ff0000000000000) 
    at /tmp/tmpT8xG1q/src.linux-x86_64-2.7/juliamodule.c:470 
#1 0x00007ffff6b6482b in iterate2 (z0=(1,1), [email protected]=0x7ffff6b60c20 <cb_func_in_iterate2__user__routines>, zmax=4, niter=256) 
    at julia.f90:38 
#2 0x00007ffff6b64897 in f2pywrapiterate2 (iterate2f2pywrap=0, z0=(1,1), [email protected]=0x7ffff6b60c20 <cb_func_in_iterate2__user__routines>, 
    zmax=4, niter=256) at /tmp/tmpT8xG1q/src.linux-x86_64-2.7/julia-f2pywrappers.f:25 
#3 0x00007ffff6b61f5e in f2py_rout_julia_iterate2 (capi_self=<optimized out>, capi_args=<optimized out>, capi_keywds=<optimized out>, 
    f2py_func=0x7ffff6b64880 <f2pywrapiterate2>) at /tmp/tmpT8xG1q/src.linux-x86_64-2.7/juliamodule.c:811 
#4 0x00000000004caaa1 in PyEval_EvalFrameEx() 
#5 0x00000000004c87a1 in PyEval_EvalCodeEx() 
#6 0x00000000005030ef in ??() 
#7 0x00000000004f8c72 in PyRun_FileExFlags() 
#8 0x00000000004f7d77 in PyRun_SimpleFileExFlags() 
#9 0x00000000004982f2 in Py_Main() 
#10 0x00007ffff6f12b45 in __libc_start_main (main=0x497d80 <main>, argc=2, argv=0x7fffffffe2a8, init=<optimized out>, fini=<optimized out>, 
    rtld_fini=<optimized out>, stack_end=0x7fffffffe298) at libc-start.c:287 
#11 0x0000000000497ca0 in _start() 
+0

Могу я просто спросить, почему вы пытаетесь использовать два разных языка, чтобы сделать что-то, что по крайней мере кажется относительно простым? Например, почему бы просто не написать все это в python? – CrazyCasta

+0

Код Fortran для функции 'iterate' примерно в 10 раз быстрее, чем его эквивалент на основе python. – taras

+0

Что касается вашей проблемы, просто чтобы быть понятным, если вы 'z = func (z)' с 'z = 4.0 + z * z', вы говорите, что это сработает? Не удалять функцию из списка параметров или что-то еще, просто делая это небольшое изменение. – CrazyCasta

ответ

3

У меня есть обходной путь, но не полное исправление, я боюсь. Ключевым моментом является то, что f2py генерирует некоторый странный вид и, возможно, только неправильный код c, когда func является функцией. Изменение func для подпрограммы, похоже, заставляет ее работать.

Чтобы сделать код работу, изменить следующим образом:

external func 
complex(kind=8) :: func 

... 

do while ((n < niter) .and. (abs(z) < zmax)) 
    z = func(z) 
... 

в

interface foo 
    subroutine func(f, z) 
    complex(kind=8), intent(out) :: f 
    complex(kind=8), intent(in) :: z 
    end subroutine func 
end interface foo 

... 

do while ((n < niter) .and. (abs(z) < zmax)) 
    call func(z, z) 
... 

Это прекрасно работает для меня, и дает правильный ответ. Если вы довольны этим, вы можете прекратить чтение. Ниже я выяснил, как это исправить.


А почему это происходит, вам нужно запустить f2py как

f2py -m modname -h iterate.pyf iterate.f90 
f2py -c --debug --build-dir build iterate.pyf iterate.f90 

Тогда мы можем отлаживать код, используя gdb python, а затем

run -c "import iterate; iterate.iterate(1.0j, lambda x: 4.0 + x*x, 4.0, 256)" 

(хотя звуками это, наверное, так или иначе произошло). Делая это, мы находим ошибку сегментации на следующей строке в build/src.linux-x86_64.3.4/iteratemodule.c (ваш каталог может отличаться):

complex_double z=(*z_cb_capi); 

z_cb_capi быть 0x0, следовательно, выдаёт ошибку сегментации. Это появляется в вспомогательной функции Cython, которая фактически вызывается, когда в коде Fortran вызывается func.Объявление функции появляется только перед строкой выше, а в варианте, где func является функцией, а не подпрограмма, это выглядит следующим образом беспорядок:

static 
#ifdef F2PY_CB_RETURNCOMPLEX 
complex_double 
#else 
void 
#endif 
cb_func_in_iterate__user__routines (
#ifndef F2PY_CB_RETURNCOMPLEX 
complex_double *return_value 
#endif 

#ifndef F2PY_CB_RETURNCOMPLEX 
, 
#endif 
complex_double *z_cb_capi) { 

, который, по существу, один из:

static void cb_func_in_iterate__user__routines (complex_double *return_value, complex_double *z_cb_capi) 
static complex_double cb_func_in_iterate__user__routines (complex_double *z_cb_capi) 

в зависимости от того, установлен или нет F2PY_CB_RETURNCOMPLEX. Похоже, что F2PY_CB_RETURNCOMPLEX никогда не определяется, потому что gdb сообщает, что функция имеет первое определение, но она вызывается, как если бы она была второй. Вы можете видеть это, потому что return_value установлен в z в коде Fortran, и ничего (NULL) не передано z_cb_capi.

Так, что в конечном итоге приводит нас ко второму пути, чтобы исправить это: передать -DF2PY_CB_RETURNCOMPLEX в f2py:

f2py -c -DF2PY_CB_RETURNCOMPLEX --debug --build-dir build iterate.pyf iterate.f90 

Это компилирует cb_func_in_iterate__user__routines во второй форме, что означает, что он действительно дозвонились правильно.

Теперь, я не думаю, что вам нужно это сделать, поэтому я скорее подозреваю, что это ошибка в f2py.

+0

Спасибо! Я также обнаружил проблему с 'z_cb_capi', но не смог двигаться дальше. – taras