ZPOCC Example — Complex Polynomial Roots

This example uses nonlinearequations.polynomial_roots_complex() (IMSL ZPOCC) to find all roots of two polynomials whose roots lie on the complex unit circle:

Polynomial 1 — 5th roots of unity:

\[p(z) = z^5 - 1 = 0\]

Roots at angles \(0°, 72°, 144°, 216°, 288°\).

Polynomial 2 — roots at 45° offsets:

\[p(z) = z^4 + 1 = 0\]

Roots at angles \(45°, 135°, 225°, 315°\).

The backend uses NumPy’s companion-matrix eigenvalue method, which handles complex coefficients natively. Coefficients are supplied in ascending degree order: [a₀, a₁, …, aₙ].

Example Code

"""IMSL ZPOCC example: complex polynomial roots.

Demonstrates :func:`nonlinearequations.polynomial_roots_complex` (IMSL ZPOCC)
on two polynomials whose roots lie on the unit circle:

  1. p(z) = z^5 - 1     (5th roots of unity)
  2. p(z) = z^4 + 1     (roots at 45°, 135°, 225°, 315° on unit circle)

Outputs:
- Table printed to stdout
- SVG plot of roots on the complex plane with unit circle
- Saved to test_output/example_imsl_poly_roots_complex.svg
"""

from __future__ import annotations

from pathlib import Path
from typing import Dict

import matplotlib.pyplot as plt
import numpy as np

from nonlinearequations import polynomial_roots_complex


def run_demo_imsl_poly_roots_complex() -> Dict[str, object]:
    """Run IMSL ZPOCC example: complex polynomial roots on the unit circle.

    Finds all roots of z^5 - 1 (5th roots of unity) and z^4 + 1 (rotated
    4th roots) using the companion-matrix eigenvalue method, then plots them
    on the complex plane together with the unit circle.

    Args:
        None

    Returns:
        Dict[str, object]: Result dict with keys ``roots_p1``
            (np.ndarray), ``roots_p2`` (np.ndarray), ``fval1`` (float),
            ``fval2`` (float), and ``plot_path`` (str).
    """
    # z^5 - 1: ascending coefficients [-1, 0, 0, 0, 0, 1]
    coeffs_p1 = np.array([-1.0 + 0j, 0, 0, 0, 0, 1.0 + 0j])
    # z^4 + 1: ascending coefficients [1, 0, 0, 0, 1]
    coeffs_p2 = np.array([1.0 + 0j, 0, 0, 0, 1.0 + 0j])

    result1 = polynomial_roots_complex(coeffs_p1)
    result2 = polynomial_roots_complex(coeffs_p2)

    roots1 = result1.x
    roots2 = result2.x

    print("\nIMSL ZPOCC Example: Complex Polynomial Roots")
    print("-" * 65)
    print("Polynomial 1: p(z) = z^5 - 1  (5th roots of unity)")
    print(f"  Max |p(root)|  = {result1.fval:.2e}   Converged: {result1.success}")
    print(f"  {'Root':<6}  {'Real':>14}  {'Imag':>14}  {'Angle (deg)':>14}")
    for z in sorted(roots1, key=lambda w: np.angle(w)):
        deg = np.degrees(np.angle(z))
        print(f"  {'':6}  {z.real:>14.8f}  {z.imag:>14.8f}  {deg:>14.4f}")
    print()
    print("Polynomial 2: p(z) = z^4 + 1  (roots at ±45° + ±135°)")
    print(f"  Max |p(root)|  = {result2.fval:.2e}   Converged: {result2.success}")
    print(f"  {'Root':<6}  {'Real':>14}  {'Imag':>14}  {'Angle (deg)':>14}")
    for z in sorted(roots2, key=lambda w: np.angle(w)):
        deg = np.degrees(np.angle(z))
        print(f"  {'':6}  {z.real:>14.8f}  {z.imag:>14.8f}  {deg:>14.4f}")
    print("-" * 65)

    # ------------------------------------------------------------------
    # Plot: complex plane with unit circle and root markers
    # ------------------------------------------------------------------
    output_dir = Path("test_output")
    output_dir.mkdir(parents=True, exist_ok=True)
    plot_path = output_dir / "example_imsl_poly_roots_complex.svg"

    theta = np.linspace(0, 2 * np.pi, 500)
    unit_x = np.cos(theta)
    unit_y = np.sin(theta)

    fig, axes = plt.subplots(1, 2, figsize=(12, 5.5))

    for ax, roots, title, poly_label, color in [
        (axes[0], roots1, r"$z^5 - 1 = 0$  (5th roots of unity)", "p(z) = z⁵ − 1", "#0e7490"),
        (axes[1], roots2, r"$z^4 + 1 = 0$  (roots at ±45°, ±135°)", "p(z) = z⁴ + 1", "#0891b2"),
    ]:
        ax.plot(unit_x, unit_y, color="#aaaaaa", linewidth=1.2, linestyle="--", label="Unit circle")
        ax.axhline(0, color="#cccccc", linewidth=0.7)
        ax.axvline(0, color="#cccccc", linewidth=0.7)
        ax.scatter(roots.real, roots.imag, color=color, s=120, zorder=5,
                   edgecolors="white", linewidths=0.8, label=f"Roots of {poly_label}")
        for z in roots:
            deg = np.degrees(np.angle(z))
            ax.annotate(
                f"{deg:.0f}°",
                xy=(z.real, z.imag),
                xytext=(z.real * 1.18, z.imag * 1.18),
                fontsize=8,
                ha="center",
                va="center",
                color=color,
            )
        ax.set_aspect("equal")
        ax.set_xlim(-1.6, 1.6)
        ax.set_ylim(-1.6, 1.6)
        ax.set_xlabel("Re(z)")
        ax.set_ylabel("Im(z)")
        ax.set_title(title)
        ax.legend(fontsize=8)
        ax.grid(True, alpha=0.20)

    fig.suptitle("IMSL ZPOCC: Complex Polynomial Roots on the Unit Circle", fontsize=13, fontweight="bold")
    fig.tight_layout()
    fig.savefig(plot_path, format="svg")
    plt.close(fig)

    return {
        "roots_p1": roots1,
        "roots_p2": roots2,
        "fval1": result1.fval,
        "fval2": result2.fval,
        "plot_path": str(plot_path),
    }


if __name__ == "__main__":
    run_demo_imsl_poly_roots_complex()

Plot Output

Complex plane plots showing roots of z^5-1 and z^4+1 on the unit circle

Left: five roots of \(z^5 - 1\) equally spaced at 72° intervals. Right: four roots of \(z^4 + 1\) at 45° offsets. Grey dashed circle is the unit circle \(|z| = 1\).

Console Output

IMSL ZPOCC Example: Complex Polynomial Roots
-----------------------------------------------------------------
Polynomial 1: p(z) = z^5 - 1  (5th roots of unity)
  Max |p(root)|  = 3.27e-15   Converged: True
  Root              Real            Imag     Angle (deg)
             -0.80901699     -0.58778525       -144.0000
              0.30901699     -0.95105652        -72.0000
              1.00000000      0.00000000          0.0000
              0.30901699      0.95105652         72.0000
             -0.80901699      0.58778525        144.0000

Polynomial 2: p(z) = z^4 + 1  (roots at ┬▒45┬░ + ┬▒135┬░)
  Max |p(root)|  = 1.68e-15   Converged: True
  Root              Real            Imag     Angle (deg)
             -0.70710678     -0.70710678       -135.0000
              0.70710678     -0.70710678        -45.0000
              0.70710678      0.70710678         45.0000
             -0.70710678      0.70710678        135.0000
-----------------------------------------------------------------