Utilisez scipy.intégrer.quad pour intégrer des nombres complexes

j'utilise le scipy en ce moment.intégrer.quad pour intégrer avec succès quelques vrais intégrands. Maintenant une situation est apparue que j'ai besoin d'intégrer un integrand complexe. quad ne semble pas être capable de le faire, comme l'autre scipy.intégrer des routines, donc je me demande: y a-t-il un moyen d'intégrer un integrand complexe en utilisant scipy.intégrer, sans avoir à séparer l'intégrale dans le réel et l'imaginaire?

25
demandé sur PeeHaa 2011-05-11 18:08:20
la source

2 ответов

Qu'y a-t-il de mal à le séparer en parties réelles et imaginaires? scipy.integrate.quad nécessite les flotteurs de retour de fonction intégrés (alias nombres réels) pour l'algorithme qu'il utilise.

import scipy
from scipy.integrate import quad

def complex_quadrature(func, a, b, **kwargs):
    def real_func(x):
        return scipy.real(func(x))
    def imag_func(x):
        return scipy.imag(func(x))
    real_integral = quad(real_func, a, b, **kwargs)
    imag_integral = quad(imag_func, a, b, **kwargs)
    return (real_integral[0] + 1j*imag_integral[0], real_integral[1:], imag_integral[1:])

par exemple,

>>> complex_quadrature(lambda x: (scipy.exp(1j*x)), 0,scipy.pi/2)
((0.99999999999999989+0.99999999999999989j),
 (1.1102230246251564e-14,),
 (1.1102230246251564e-14,))

ce qui est ce que vous vous attendez à arrondir erreur - intégrale de exp(I x) à partir de 0, pi/2 est (1/i)(e^i pi/2 - e^0) = -i(i - 1) = 1 + i ~ (0.9999999999999999989+0.999999999999989 j).

et pour rappel au cas où ce ne serait pas clair à 100% pour tout le monde, l'intégration est une fonction linéaire, c'est-à-dire que HF(x) + k g(x) } dx = HF(x) dx + k g(x) dx (où k est une constante par rapport à x). Ou pour notre cas précis ∫ z(x) dx = ∫ Re z(x) dx + i ∫ Im z(x) dx z(x) = Re z(x) + i Im(z x).

si vous essayez de faire une intégration sur un chemin dans le plan complexe (autre que le long de l'axe réel) ou une région dans le plan complexe, vous aurez besoin d'un algorithme plus sophistiqué.

Remarque: Scipy.intégrer ne sera pas directement gérer l'intégration complexe. Pourquoi? Il fait le levage lourd dans le FORTRAN QUADPACK bibliothèque, plus précisément dans qagse.f qui exige explicitement que les fonctions / variables soient réelles avant de faire sa " quadrature adaptative globale basée sur la quadrature de 21 points Gauss-Kronrod à l'intérieur de chaque sous-interval, avec accélération par L'algorithme epsilon de Peter Wynn."Donc, à moins que vous ne vouliez essayer de modifier le FORTRAN sous-jacent pour l'amener à gérer des nombres complexes, compilez - le dans un nouvelle bibliothèque, tu ne vas pas la faire marcher.

Si vous voulez vraiment faire de Gauss-Kronrod méthode des nombres complexes dans exactement une intégration, regardez wikipédia et mettre en oeuvre directement comme fait ci-dessous (en utilisant la règle 15-pt, 7-pt). Note, je memoize fonction pour répéter les appels communs aux variables communes (en supposant que les appels de fonction sont lents comme si la fonction est très complexe). Aussi seulement 7-pt et 15-pt règle, puisque je ne me sens pas de calculer le les noeuds / poids moi-même et ceux étaient ceux énumérés sur wikipedia, mais obtenir des erreurs raisonnables pour les cas de test (~1e-14)

import scipy
from scipy import array

def quad_routine(func, a, b, x_list, w_list):
    c_1 = (b-a)/2.0
    c_2 = (b+a)/2.0
    eval_points = map(lambda x: c_1*x+c_2, x_list)
    func_evals = map(func, eval_points)
    return c_1 * sum(array(func_evals) * array(w_list))

def quad_gauss_7(func, a, b):
    x_gauss = [-0.949107912342759, -0.741531185599394, -0.405845151377397, 0, 0.405845151377397, 0.741531185599394, 0.949107912342759]
    w_gauss = array([0.129484966168870, 0.279705391489277, 0.381830050505119, 0.417959183673469, 0.381830050505119, 0.279705391489277,0.129484966168870])
    return quad_routine(func,a,b,x_gauss, w_gauss)

def quad_kronrod_15(func, a, b):
    x_kr = [-0.991455371120813,-0.949107912342759, -0.864864423359769, -0.741531185599394, -0.586087235467691,-0.405845151377397, -0.207784955007898, 0.0, 0.207784955007898,0.405845151377397, 0.586087235467691, 0.741531185599394, 0.864864423359769, 0.949107912342759, 0.991455371120813]
    w_kr = [0.022935322010529, 0.063092092629979, 0.104790010322250, 0.140653259715525, 0.169004726639267, 0.190350578064785, 0.204432940075298, 0.209482141084728, 0.204432940075298, 0.190350578064785, 0.169004726639267, 0.140653259715525,  0.104790010322250, 0.063092092629979, 0.022935322010529]
    return quad_routine(func,a,b,x_kr, w_kr)

class Memoize(object):
    def __init__(self, func):
        self.func = func
        self.eval_points = {}
    def __call__(self, *args):
        if args not in self.eval_points:
            self.eval_points[args] = self.func(*args)
        return self.eval_points[args]

def quad(func,a,b):
    ''' Output is the 15 point estimate; and the estimated error '''
    func = Memoize(func) #  Memoize function to skip repeated function calls.
    g7 = quad_gauss_7(func,a,b)
    k15 = quad_kronrod_15(func,a,b)
    # I don't have much faith in this error estimate taken from wikipedia
    # without incorporating how it should scale with changing limits
    return [k15, (200*scipy.absolute(g7-k15))**1.5]

cas de Test:

>>> quad(lambda x: scipy.exp(1j*x), 0,scipy.pi/2.0)
[(0.99999999999999711+0.99999999999999689j), 9.6120083407040365e-19]

Je ne fais pas confiance à l'estimation d'erreur -- j'ai pris quelque chose du wiki pour l'estimation d'erreur recommandée lors de l'intégration de [-1 à 1] et les valeurs ne me semblent pas raisonnables. Par exemple, l'erreur ci-dessus comparée à la vérité est ~5e-15 pas ~1e-19. Je suis sûr que si quelqu'un consultait les recettes de num, vous pourriez obtenir un plus précis estimer. (Probablement plusieurs par (a-b)/2 à une certaine puissance ou quelque chose de similaire).

rappel, la version python est moins précise que de simplement appeler L'intégration QUADPACK de scipy deux fois. (Vous pourriez l'améliorer si vous le souhaitez).

39
répondu dr jimbob 2011-05-12 17:56:29
la source

je me rends compte que je suis en retard à la fête, mais peut-être quadpy (un de mes projets) peut vous aider. Ce

import numpy
import quadpy
import scipy

val = quadpy.line_segment.integrate(
        lambda x: scipy.exp(1j*x),
        [0, 1],
        quadpy.line_segment.GaussKronrod(3)
        )

print(val)

correctement donne

(0.841470984808+0.459697694132j)

au Lieu de GaussKronrod(3), vous pouvez utiliser n'importe quel autre régime.

3
répondu Nico Schlömer 2018-01-22 18:56:23
la source

Autres questions sur