Transform integral with complex bounds

72 Views Asked by At

Question

I have a line integral given by

$$ \begin{align} I &= \int_{z_0=a+ib}^{z_1=c+id}dz~ z \\ \\ &= -\frac{a^2}{2}+\frac{b^2}{2}-\frac{d^2}{2}+\frac{c^2}{2}+i(-ab+cd) \end{align} $$

For this question I made the integrand $f(z)=z$ for easy math, but in practice it will be non-analytic. I intend on solving this integral in Python with scipy, but in order to do that, I need to be able to represent everything in terms of real values (e.g. $z\equiv\text{Re}(z)+i\text{Im}(z)$).

Normally this would be easy, as I could just break the integrand into real and imaginary parts before distributing the integral (e.g. $\int dz~\text{Re}(z)+i\int dz~\text{Im}(z)$), however the bounds are complex, and I'm not confident of a way to either transform or represent them in terms of real values.

How do I put this into a form where the bounds are no longer complex?

Set-up

This question has been helpful in breaking the integral up into three separate parts, so that I get $$ I = \underbrace{\int_{z_0}^{a}dz~f(z)}_{I_1}+\underbrace{\int_{a}^{c}dz~f(z)}_{I_2}+\underbrace{\int_{c}^{z_1}dz~f(z)}_{I_3}, $$ such that it gives the same result as the normal way of evaluating $f(z)$. This question from Stack Overflow seems to solve the second part of my question, which handles the bounds by further breaking the first and last integrals into two more parts each so that I get $$ I = \overbrace{\int_{0}^{a}dz~\text{Re}\{f(z+z_0)\}+\int_{0}^{a}dz~\text{Im}\{f(z+z_0)\}}^{I_1}+\overbrace{\int_{a}^{c}dz~f(z)}^{I_2}+\overbrace{\int_{c}^{0}dz~\text{Re}\{f(z+z_1)\}+\int_{c}^{0}dz~\text{Im}\{f(z+z_1)\}}^{I_3}. $$

Here, everything is in terms of values I can easily plug into scipy- but when I evaluate this, it gives me the incorrect result: $I = a^2-c^2+i(ab-cd)$. As a sanity check I put it into sympy and came to the same conclusion

from sympy import *
a, b, c, d = symbols('a b c d', real=True)
t = Symbol('t', real = True)
f = t

z0 = a+I*b
z1 = c+I*d

I0 = integrate(f, (t, z0, z1)).doit()
actual = re(I0)+I*im(I0)

I1 = integrate(f, (t, z0, a))
I2= integrate(f, (t, a, c))
I3 = integrate(f, (t, c, z1))
cauchy = re(I1+I2+I3)+I*im(I1+I2+I3)

print(actual==cauchy) # prints true

Ia = integrate(re(f.subs({t:t+z0})), (t, 0, a))
Ib = integrate(im(f.subs({t:t+z0})), (t, 0, a))
Ic = integrate(f, (t, a, c))
Id = integrate(re(f.subs({t:t+z1})), (t, c, 0))
Ie = integrate(im(f.subs({t:t+z1})), (t, c, 0))
test = re(Ia+I*Ib+Ic+Id+I*Ie)+I*im(Ia+I*Ib+Ic+Id+I*Ie)

print(actual==test) # prints false

My math after the first expansion is definitely wrong, but the issue is that I don't know how to correct it. Could someone nudge me in the right direction?

1

There are 1 best solutions below

0
On BEST ANSWER

@aschelper, @orangeskid, and @MPW pointed me in the right direction by explaining that the question I was trying to generalize, "How do I integrate a non-analytic function between two points?" is fundamentally non-trivial. This is standard knowledge in complex analysis that I missed when looking into this class of problem. The path becomes critical to obtaining a good solution, and knowledge of the nature of the integrand in the complex plane is very important to choosing a valid path.

I still want to provide an answer, as the second part of my question, "How do I change the bounds to be real-valued" was never addressed, and I feel as though someone encountering this issue in the future could benefit from it.

A complex line integral along some path parameterized by $\gamma(t)$ is defined by $$ \int_{\gamma}dz~f(z):=\int_{a}^{b}dt~f(\gamma(t))\gamma^{\prime}(t), $$ where $a$ and $b$ are end-points of the parameterization.

For my test-case, I choose a linear path from $z_0$ to $z_1$ such that $\gamma(t)=(1-t)z_0+t z_1$ on the interval $0<t<1$, where $\gamma(0)=z_0$ and $\gamma(1)=z_1$. My integral is now

$$ I = \int_{0}^{1}dt~\left[(1-t)z_0+t z_1\right]\left[z_1-z_0\right]. $$

This makes my bounds real-valued, and I can break the integral into real and imaginary parts as stated in my problem. In sympy, I confirm the result (using my code from the question)

f_of_gamma = (1-t)*z0+t*z1
gamma_prime = z1-z0
integrand = f_of_gamma*gamma_prime

path_int = integrate(integrand, (t, 0, 1))
test = re(path_int)+I*im(path_int)
print(test==actual) # true

Putting this into a numerical solver gives the same result as the analytic answer

from scipy.integrate import quad
from numpy import real, imag

def integrand(t, za, zb):
    def f_of_gamma(t):
        return (1-t)*za+t*zb
    def gamma_prime(t):
        return zb-za
    return f_of_gamma(t)*gamma_prime(t)

def integral(start, stop):
    def real_integrand(t, *args):
        return real(integrand(t, *args))
    
    def imag_integrand(t, *args):
        return imag(integrand(t, *args))
    
    real_result = quad(real_integrand, 0, 1, args=(start, stop))[0]
    imag_result = quad(imag_integrand, 0, 1, args=(start, stop))[0]
    return real_result+1j*imag_result

za, zb = 1+1j*3, 3-1j*5
numerical = integral(za, zb)
print(f"Scipy found: {numerical}, \
        Sympy found: ({actual.subs({a:za.real, b:za.imag, c:zb.real, d:zb.imag})})")

Answer = (-4-8i).

In summary, nothing revolutionary was discovered here, but I did figure out the question of "how do I transform the bounds". It turns out that there is a well-established definition for the complex path integral that uses parameterization to handle it. For my first question, I'll need to think deeply about the way I can choose a path for a non-analytic function before tackling the problem- something that won't be accomplished on this forum.

Thanks all for the help.