2016-04-20 15 views
0

Ich arbeite am fraktalen Generator-Code. Der Hauptcode ist in Python geschrieben und Iterationen Teil ist in Fortran geschrieben. Ich verwende f2py, um die zwei Codes zusammen zu kleben. Hier ist die Fortran-Funktion, die ich verwende:segfault auf 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 

Hier ist die docstring für den generierten Wrapper-Code:

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 

Ich erhalte Segmentation fault Fehler, wenn zu Verwendung iterate mit jeder Python-Callback-Funktion zu versuchen. Hier ist ein Beispiel Ergebnis, das ich bekommen:

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

ich durch alle verfügbaren Unterlagen über Rückrufe in f2py ausgesehen haben, aber haben keine Lösung für dieses Problem gefunden. Jede Hilfe wäre willkommen.

UPDATE

ist hier ein Backtrace von GDB:

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

Kann ich nur fragen, warum versuchen Sie, zwei verschiedene Sprachen zu verwenden, um etwas zu tun, das zumindest relativ einfach scheint? Zum Beispiel, warum schreibst du nicht alles in Python? – CrazyCasta

+0

Der Fortran-Code für die 'iterate'-Funktion ist etwa 10-mal schneller als sein Python-Äquivalent. – taras

+0

Was Ihr Problem angeht, nur um klar zu sein, wenn Sie 'z = func (z)' mit 'z = 4.0 + z * z' Sie sagen, es wird funktionieren? Entfernen Sie die Funktion nicht aus der Parameterliste oder irgendetwas, machen Sie nur diese kleine Änderung. – CrazyCasta

Antwort

3

ich eine Abhilfe haben, aber keine vollständige Lösung, ich habe Angst. Der Schlüsselpunkt scheint zu sein, dass f2py einige seltsam aussehende, und möglicherweise nur inkorrekt, c-Code erzeugt, wenn func eine Funktion ist. Das Ändern von func zu einem Unterprogramm scheint es zu funktionieren.

Um Ihren Code funktioniert, Folgendes ändern:

external func 
complex(kind=8) :: func 

... 

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

zu

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) 
... 

Dies funktioniert gut für mich, und gibt die richtige Antwort. Wenn Sie damit zufrieden sind, können Sie aufhören zu lesen. Im Folgenden habe ich herausgefunden, wie ich es beheben kann.


Was, warum dies geschieht, müssen Sie f2py wie

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

laufen Dann können wir den Code debuggen gdb python verwenden und dann

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

(obwohl von den Klängen der es ist wahrscheinlich so weit gekommen). Dadurch finden wir es in der folgenden Zeile Segfaults in build/src.linux-x86_64.3.4/iteratemodule.c (Ihr Verzeichnis könnte anders sein):

complex_double z=(*z_cb_capi); 

z_cb_capi0x0 zu sein, daher der segfault. Dies erscheint in der Cython-Hilfsfunktion, die tatsächlich aufgerufen wird, wenn im Fortran-Code func aufgerufen wird.Die Funktionsdeklaration erscheint kurz vor der Linie oben und in der Version, wo func ist eine Funktion eher als ein Unterprogramm, es sieht aus wie das folgende Chaos:

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) { 

, die im wesentlichen eines von:

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) 

abhängig davon, ob F2PY_CB_RETURNCOMPLEX definiert ist oder nicht. Es scheint, dass F2PY_CB_RETURNCOMPLEX niemals definiert wird, weil gdb die Funktion so meldet, als hätte sie die erste Definition, aber sie wird so aufgerufen, als wäre sie die zweite. Sie können dies sehen, weil return_value im Fortran-Code auf die Adresse z gesetzt ist und nichts (NULL) an z_cb_capi übergeben wird.

Also, die schließlich führt uns zum zweiten Weg, dies zu beheben: pass -DF2PY_CB_RETURNCOMPLEX-f2py:

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

Dieses dann cb_func_in_iterate__user__routines in der zweiten Form kompiliert, das heißt, es richtig genannt bekommt.

Nun, ich denke nicht, dass Sie dies tun sollten, also vermute ich, dass dies ein Fehler in f2py ist.

+0

Danke! Ich habe das Problem mit 'z_cb_capi' ebenfalls entdeckt, aber es ist mir nicht gelungen, weiter zu gehen. – taras