EVCRG Example — Eigenvectors of a Symmetric Matrix

This example creates a 3x3 random symmetric matrix (seed=42) and computes its eigenvalues and right eigenvectors using eigensystems.evcrg(), verifying the relation \(A v = \lambda v\).

Example Code

"""IMSL EVCRG example: eigenvalues and eigenvectors of a symmetric matrix.

Reproduces the IMSL EVCRG style example:
- Create a 3x3 random symmetric matrix (seed=42).
- Compute eigenvalues + eigenvectors with evcrg().
- Scatter plot: eigenvector components colored by eigenvalue magnitude.
- Save to test_output/example_imsl_evcrg.svg

Outputs:
- Printed table of eigenpairs
- SVG scatter plot saved to test_output/
"""
from __future__ import annotations

from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np

from eigensystems import evcrg


def run_demo_evcrg() -> dict:
    """Run IMSL EVCRG example: eigenpairs of a 3x3 symmetric matrix.

    Creates a 3x3 symmetric matrix from a random seed, computes its
    eigenvalues and eigenvectors, verifies A @ v = λ v, and produces
    a scatter plot of eigenvector components coloured by eigenvalue magnitude.

    Args:
        None

    Returns:
        dict: Result dictionary with keys ``eigenvalues`` (np.ndarray),
            ``eigenvectors`` (np.ndarray), and ``plot_path`` (str).
    """
    rng = np.random.default_rng(42)
    base = rng.random((3, 3))
    a = (base + base.T) / 2.0 + 3.0 * np.eye(3)

    result = evcrg(a)
    lam = result.eigenvalues
    vecs = result.eigenvectors

    print("\nIMSL EVCRG Example: 3x3 Random Symmetric Matrix")
    print("-" * 55)
    print("Matrix A:")
    for row in a:
        print("  " + "  ".join(f"{v:8.4f}" for v in row))
    print("-" * 55)
    print(f"{'i':<4} {'Eigenvalue':>14} {'|v1|':>8} {'|v2|':>8} {'|v3|':>8}")
    print("-" * 55)
    for i, (l_val, vec) in enumerate(zip(lam, vecs.T)):
        print(f"  {i+1:<3} {l_val.real:>14.6f}  "
              f"{abs(vec[0]):>8.4f}  {abs(vec[1]):>8.4f}  {abs(vec[2]):>8.4f}")
    print("-" * 55)

    # Verify Av = lam*v
    for i, (l_val, vec) in enumerate(zip(lam, vecs.T)):
        residual = np.linalg.norm(a @ vec - l_val * vec)
        print(f"  Eigenpair {i+1}: ||Av - lam*v|| = {residual:.2e}")

    output_dir = Path("test_output")
    output_dir.mkdir(parents=True, exist_ok=True)
    plot_path = output_dir / "example_imsl_evcrg.svg"

    fig, ax = plt.subplots(figsize=(7, 5))
    colors = plt.cm.coolwarm(np.linspace(0.1, 0.9, len(lam)))
    for i, (l_val, col) in enumerate(zip(lam, colors)):
        vec = vecs[:, i].real
        xs = np.arange(len(vec))
        ax.scatter(xs, vec, color=col, s=120, zorder=5,
                   label=f"lam{i+1}={l_val.real:.3f}")
        ax.plot(xs, vec, color=col, alpha=0.6)

    ax.set_xticks([0, 1, 2])
    ax.set_xticklabels(["v1", "v2", "v3"])
    ax.set_xlabel("Eigenvector component")
    ax.set_ylabel("Component value")
    ax.set_title("IMSL EVCRG: Eigenvector components (coloured by eigenvalue)")
    ax.legend(title="Eigenvalues")
    ax.axhline(0, color="gray", linewidth=0.8, linestyle="--")
    fig.tight_layout()
    fig.savefig(plot_path, format="svg")
    plt.close(fig)
    print(f"\nPlot saved: {plot_path}")

    return {
        "eigenvalues": lam,
        "eigenvectors": vecs,
        "plot_path": str(plot_path),
    }


if __name__ == "__main__":
    run_demo_evcrg()

Plot Output

Scatter plot of eigenvector components colored by eigenvalue

Eigenvector components for each eigenpair. Points are coloured by eigenvalue magnitude; lines connect components of the same eigenvector.

Console Output

IMSL EVCRG Example: 3x3 Random Symmetric Matrix
-------------------------------------------------------
Matrix A:
    3.7740    0.5681    0.8099
    0.5681    3.0942    0.8808
    0.8099    0.8808    3.1281
-------------------------------------------------------
i        Eigenvalue     |v1|     |v2|     |v3|
-------------------------------------------------------
  1         4.869528    0.6680    0.4911    0.5591
  2         2.921770    0.7282    0.5864    0.3549
  3         2.204948    0.1535    0.6442    0.7493
-------------------------------------------------------
  Eigenpair 1: ||Av - lam*v|| = 1.33e-15
  Eigenpair 2: ||Av - lam*v|| = 1.79e-15
  Eigenpair 3: ||Av - lam*v|| = 5.98e-16

Plot saved: test_output\example_imsl_evcrg.svg