Core issues experienced when attempting to debug python code below were resolved under guidance of Google's AI Assistant .
Principle of Argument doesn't provide any information about values of polynomial zeros inside predefined circle as well as locations of these zeros on complex plane. Classic math just says how many polynomial zeros we get inside given circle and nothing else.
Python calculates the Principle of Argument
(or argument principle in complex analysis) by computing the winding
number, which equals the number of zeros minus poles inside a contour.
This is implemented by evaluating the change in the argument (angle) of a
function along a closed contour, usually using numpy for angle calculation.cxroots imported in python module provides object Circle having method roots to achieve the goal to get actual zeros values.
Areas of physics and engineering that require not only the number of zeros (roots) of a complex polynomial within a certain region, but their exact values as well, are typically those involving system stability analysis, resonant behavior, signal processing, and wave propagation. In these scenarios, the exact values determine system frequency, decay rates, and phase behavior.
pythonComplex01.py
import numpy as np
from cxroots import Circle
def count_zeros(f, df, contour_points):
"""
Counts zeros of f(z) within the region enclosed by contour_points.
f: The analytic function
df: The derivative of the function
contour_points: Array of complex numbers forming a closed loop
"""
# Evaluate f and f' at each point on the contour
fz = f(contour_points)
dfz = df(contour_points)
# Calculate the integrand: f'(z) / f(z)
integrand = dfz / fz
# Calculate dz (the difference between consecutive points)
dz = np.diff(contour_points, append=contour_points[0])
# Compute the contour integral using the trapezoidal rule
integral = np.sum(integrand * dz)
# The result should be an integer
return int(np.round((integral / (2j * np.pi)).real))
t = np.linspace(0, 2 * np.pi, 1000)
circle = 4 * np.exp(1j * t)
f = lambda z: z**4 + 4*z**3 - 8*z - 2
df = lambda z: 4*z**3 + 12*z**2 -8
print("The Argument Principle and the Logarithmic Derivative in Complex Analysis\n")
print("f(z) = z**4 + 4*z**3 - 8*z - 2 ; df/dz(z) = 4*z**3 + 12*z**2 -8\n")
print(f"Number of zeros in circle of radius 4: {count_zeros(f, df, circle)}\n")
# Define a circle of radius 4 centered at the origin
C = Circle(0, 4)
# Find all roots within the circle
roots = C.roots(f, df)
print(roots,"\n")
circle = 1 * np.exp(1j * t)
print(f"Number of zeros in circle of radius 1: {count_zeros(f, df, circle)}\n")
# Define a circle of radius 3 centered at the origin
C = Circle(0, 1)
# Find all roots within the circle
roots = C.roots(f, df)
print(roots)
~/ComplexAnalysis
❯ python pythonComplex01.py
The Argument Principle and the Logarithmic Derivative in Complex Analysis
f(z) = z**4 + 4*z**3 - 8*z - 2 ; df/dz(z) = 4*z**3 + 12*z**2 -8
Number of zeros in circle of radius 4: 4
Multiplicity | Root
------------------------------------------------
1 | -3.334414218339 +0.000000000000i
1 | -1.741963784303 -0.000000000000i
1 | -0.258036215697 +0.000000000000i
1 | 1.334414218339 -0.000000000000i
Number of zeros in circle of radius 1: 1
Multiplicity | Root
------------------------------------------------
1 | -0.258036215697 +0.000000000000i
~/ComplexAnalysis
Case 2
> cat pythonComplex02.pyimport numpy as np
from cxroots import Circle
def count_zeros(f, df, contour_points):
"""
Counts zeros of f(z) within the region enclosed by contour_points.
f: The analytic function
df: The derivative of the function
contour_points: Array of complex numbers forming a closed loop
"""
# Evaluate f and f' at each point on the contour
fz = f(contour_points)
dfz = df(contour_points)
# Calculate the integrand: f'(z) / f(z)
integrand = dfz / fz
# Calculate dz (the difference between consecutive points)
dz = np.diff(contour_points, append=contour_points[0])
# Compute the contour integral using the trapezoidal rule
integral = np.sum(integrand * dz)
# The result should be an integer
return int(np.round((integral / (2j * np.pi)).real))
t = np.linspace(0, 2 * np.pi, 1000)
circle = 3 * np.exp(1j * t)
f = lambda z: z**13 + 5*z + 2
df = lambda z: 13*z**12 + 5
print("The Argument Principle and the Logarithmic Derivative in Complex Analysis\n")
print("f(z) = z**13 +5*z +2 ; df/dz(z) = 13*z**12 + 5\n")
print(f"Number of zeros in circle of radius 3: {count_zeros(f, df, circle)}")
# Define a circle of radius 3 centered at the origin
C = Circle(0, 3)
# Find all roots within the circle
roots = C.roots(f, df)
print(roots)
circle = 1 * np.exp(1j * t)
print(f"Number of zeros in circle of radius 1: {count_zeros(f, df, circle)}")
# Define a circle of radius 3 centered at the origin
C = Circle(0, 1)
# Find all roots within the circle
roots = C.roots(f, df)
print(roots)
~/ComplexAnalysis
❯ python pythonComplex02.py
The Argument Principle and the Logarithmic Derivative in Complex Analysis
f(z) = z**13 +5*z +2 ; df/dz(z) = 13*z**12 + 5
Number of zeros in circle of radius 3: 13
RootError encountered when subdivding Annulus sector: center=0.000+0.000i, r0=1.129, r1=1.221, phi0=7.414, phi1=8.168 into:
Annulus sector: center=0.000+0.000i, r0=1.129, r1=1.156, phi0=7.414, phi1=8.168
Annulus sector: center=0.000+0.000i, r0=1.156, r1=1.221, phi0=7.414, phi1=8.168
Multiplicity | Root
------------------------------------------------
1 | -1.063154526530 -0.299112694677i
1 | -1.063154526530 +0.299112694677i
1 | -0.771405797002 -0.815087403529i
1 | -0.771405797002 +0.815087403529i
1 | -0.399998657881 +0.000000000000i
1 | -0.262722673288 -1.111020328751i
1 | -0.262722673288 +1.111020328751i
1 | 0.326597088580 -1.109580758477i
1 | 0.326597088580 +1.109580758477i
1 | 0.837706346436 -0.811700768559i
1 | 0.837706346436 +0.811700768559i
1 | 1.132978890745 +0.297012150385i
1 | 1.132978890745 -0.297012150385i
Number of zeros in circle of radius 1: 1
Multiplicity | Root
------------------------------------------------
1 | -0.399998657881 +0.000000000000i
=======================================================
Now attempt plotting of samples below. First add libraries to python VENV
======================================================
> pip install PyQt6
> pip install matplotlib
❯ cat pythonComplex04.py
import os
# Silence Qt font/logging warnings
os.environ["QT_LOGGING_RULES"] = "*.debug=false;qt.qpa.fonts.warning=false"
from cxroots import Circle
import matplotlib
import matplotlib.pyplot as plt
f = lambda z: z**4 + 4*z**3 - 8*z - 2
df = lambda z: 4*z**3 + 12*z**2 -8
# 2. Define a contour (e.g., a circle with radius 4 centered at 0)
C = Circle(0, 4)
# 3. Plot the contour itself to verify the search region
# 4. Find roots and plot them within the contour
roots = C.roots(f, df)
print(roots)
roots.show()
plt.show()
> cat pythonComplex03.py
import os
# Silence Qt font/logging warnings
os.environ["QT_LOGGING_RULES"] = "*.debug=false;qt.qpa.fonts.warning=false"
from cxroots import Circle
import matplotlib
import matplotlib.pyplot as plt
f = lambda z: z**13 + 5*z + 2
df = lambda z: 13*z**12 + 5
# 2. Define a contour (e.g., a circle with radius 3 centered at 0)
C = Circle(0, 3)
# 3. Plot the contour itself to verify the search region
# 4. Find roots and plot them within the contour
roots = C.roots(f, df)
print(roots)
roots.show()
plt.show()
The script below counts number of zeros in circle of predefined radius, prints values of zeros been detected and pops up the plot containing locations of all zeros on complex plane.
❯ cat pythonComplex07.py
import os
# Silence Qt font/logging warnings
os.environ["QT_LOGGING_RULES"] = "*.debug=false;qt.qpa.fonts.warning=false"
from cxroots import Circle
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
def count_zeros(f, df, contour_points):
"""
Counts zeros of f(z) within the region enclosed by contour_points.
f: The analytic function
df: The derivative of the function
contour_points: Array of complex numbers forming a closed loop
"""
# Evaluate f and f' at each point on the contour
fz = f(contour_points)
dfz = df(contour_points)
# Calculate the integrand: f'(z) / f(z)
integrand = dfz / fz
# Calculate dz (the difference between consecutive points)
dz = np.diff(contour_points, append=contour_points[0])
# Compute the contour integral using the trapezoidal rule
integral = np.sum(integrand * dz)
# The result should be an integer
return int(np.round((integral / (2j * np.pi)).real))
t = np.linspace(0, 2 * np.pi, 1000)
circle = 4 * np.exp(1j * t)
f = lambda z: 4*z**5 + 4*z**3 - 4*z + 9
df = lambda z: 20*z**4 + 12*z**2 - 4
print("The Argument Principle and the Logarithmic Derivative in Complex Analysis\n")
print("f(z) = 4*z**5 + 4*z**3 - 4*z + 9 ; df/dz(z) = 20*z**4 + 12*z**2 - 4\n")
print(f"Number of zeros in circle of radius 4: {count_zeros(f, df, circle)}\n")
# 2. Define a contour (e.g., a circle with radius 4 centered at 0)
C = Circle(0, 4)
# 3. Plot the contour itself to verify the search region
# 4. Find roots and plot them within the contour
roots = C.roots(f, df)
print(roots)
roots.show()
plt.show()
Increasing radius from 3 to 4 fixes problem in pythonComplex02.py and pythonComplex03.py
❯ cat pythonComplex09.py
import numpy as np
from cxroots import Circle
import os
# Silence Qt font/logging warnings
os.environ["QT_LOGGING_RULES"] = "*.debug=false;qt.qpa.fonts.warning=false"
import matplotlib
import matplotlib.pyplot as plt
def count_zeros(f, df, contour_points):
"""
Counts zeros of f(z) within the region enclosed by contour_points.
f: The analytic function
df: The derivative of the function
contour_points: Array of complex numbers forming a closed loop
"""
# Evaluate f and f' at each point on the contour
fz = f(contour_points)
dfz = df(contour_points)
# Calculate the integrand: f'(z) / f(z)
integrand = dfz / fz
# Calculate dz (the difference between consecutive points)
dz = np.diff(contour_points, append=contour_points[0])
# Compute the contour integral using the trapezoidal rule
integral = np.sum(integrand * dz)
# The result should be an integer
return int(np.round((integral / (2j * np.pi)).real))
t = np.linspace(0, 2 * np.pi, 1000)
circle = 4 * np.exp(1j * t)
f = lambda z: z**13 + 5*z + 2
df = lambda z: 13*z**12 + 5
print("The Argument Principle and the Logarithmic Derivative in Complex Analysis\n")
print("f(z) = z**13 +5*z +2 ; df/dz(z) = 13*z**12 + 5\n")
print(f"Number of zeros in circle of radius 4: {count_zeros(f, df, circle)}\n")
# 2. Define a contour (e.g., a circle with radius 4 centered at 0)
C = Circle(0, 4)
# 3. Plot the contour itself to verify the search region
# 4. Find roots and plot them within the contour
roots = C.roots(f, df)
print(roots)
roots.show()
plt.show()





