Cython optimieren Wurzel-Suche API#

Die zugrundeliegenden C-Funktionen für die folgenden Wurzel-Sucher können direkt über Cython aufgerufen werden

Die Cython-API für die Wurzel-Suchfunktionen ist ähnlich, mit dem Unterschied, dass es kein disp-Argument gibt. Importieren Sie die Wurzel-Suchfunktionen mit cimport aus scipy.optimize.cython_optimize.

from scipy.optimize.cython_optimize cimport bisect, ridder, brentq, brenth

Callback-Signatur#

Die Nullstellen-Funktionen in cython_optimize erwarten einen Callback, der eine Gleitkommazahl für die skalare unabhängige Variable als 1. Argument und eine benutzerdefinierte struct mit zusätzlichen Parametern als 2. Argument entgegennimmt.

double (*callback_type)(double, void*) noexcept

Beispiele#

Die Verwendung von cython_optimize erfordert Cython, um Callbacks zu schreiben, die in C kompiliert werden. Weitere Informationen zur Kompilierung von Cython finden Sie in der Cython-Dokumentation.

Dies sind die grundlegenden Schritte

  1. Erstellen Sie eine Cython .pyx-Datei, zum Beispiel: myexample.pyx.

  2. Importieren Sie den gewünschten Wurzel-Sucher aus cython_optimize.

  3. Schreiben Sie die Callback-Funktion und rufen Sie die ausgewählte Wurzel-Suchfunktion auf, wobei Sie den Callback, alle zusätzlichen Argumente und die anderen Solver-Parameter übergeben.

    from scipy.optimize.cython_optimize cimport brentq
    
    # import math from Cython
    from libc cimport math
    
    myargs = {'C0': 1.0, 'C1': 0.7}  # a dictionary of extra arguments
    XLO, XHI = 0.5, 1.0  # lower and upper search boundaries
    XTOL, RTOL, MITR = 1e-3, 1e-3, 10  # other solver parameters
    
    # user-defined struct for extra parameters
    ctypedef struct test_params:
        double C0
        double C1
    
    
    # user-defined callback
    cdef double f(double x, void *args) noexcept:
        cdef test_params *myargs = <test_params *> args
        return myargs.C0 - math.exp(-(x - myargs.C1))
    
    
    # Cython wrapper function
    cdef double brentq_wrapper_example(dict args, double xa, double xb,
                                       double xtol, double rtol, int mitr):
        # Cython automatically casts dictionary to struct
        cdef test_params myargs = args
        return brentq(
            f, xa, xb, <test_params *> &myargs, xtol, rtol, mitr, NULL)
    
    
    # Python function
    def brentq_example(args=myargs, xa=XLO, xb=XHI, xtol=XTOL, rtol=RTOL,
                       mitr=MITR):
        '''Calls Cython wrapper from Python.'''
        return brentq_wrapper_example(args, xa, xb, xtol, rtol, mitr)
    
  4. Wenn Sie Ihre Funktion aus Python aufrufen möchten, erstellen Sie eine Cython-Wrapper und eine Python-Funktion, die den Wrapper aufruft, oder verwenden Sie cpdef. Dann können Sie in Python das Beispiel importieren und ausführen.

    from myexample import brentq_example
    
    x = brentq_example()
    # 0.6999942848231314
    
  5. Erstellen Sie eine Cython .pxd-Datei, wenn Sie Cython-Funktionen exportieren müssen.

Vollständige Ausgabe#

Die Funktionen in cython_optimize können auch die vollständige Ausgabe vom Solver in eine C struct kopieren, die als letztes Argument übergeben wird. Wenn Sie keine vollständige Ausgabe wünschen, übergeben Sie einfach NULL. Die vollständige Ausgabe-struct muss vom Typ zeros_full_output sein, welche in scipy.optimize.cython_optimize mit den folgenden Feldern definiert ist

  • int funcalls: Anzahl der Funktionsaufrufe

  • int iterations: Anzahl der Iterationen

  • int error_num: Fehlernummer

  • double root: Wurzel der Funktion

Die Wurzel wird von cython_optimize in die vollständige Ausgabe-struct kopiert. Eine Fehlernummer von -1 bedeutet einen Vorzeichenfehler, -2 einen Konvergenzfehler und 0 bedeutet, dass der Solver konvergiert ist. Fortsetzung des vorherigen Beispiels

from scipy.optimize.cython_optimize cimport zeros_full_output


# cython brentq solver with full output
cdef zeros_full_output brentq_full_output_wrapper_example(
        dict args, double xa, double xb, double xtol, double rtol,
        int mitr):
    cdef test_params myargs = args
    cdef zeros_full_output my_full_output
    # use my_full_output instead of NULL
    brentq(f, xa, xb, &myargs, xtol, rtol, mitr, &my_full_output)
    return my_full_output


# Python function
def brent_full_output_example(args=myargs, xa=XLO, xb=XHI, xtol=XTOL,
                              rtol=RTOL, mitr=MITR):
    '''Returns full output'''
    return brentq_full_output_wrapper_example(args, xa, xb, xtol, rtol,
                                              mitr)

result = brent_full_output_example()
# {'error_num': 0,
#  'funcalls': 6,
#  'iterations': 5,
#  'root': 0.6999942848231314}