Monday, 30 March 2026

Python calculation based on principle of argument (theory of functions of a complex variable)

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


Python calculation based on principle of argument (theory of functions of a complex variable)

  Core issues experienced when attempting to debug python code below were resolved under guidance of Google's AI Assistant .  Principle ...