NYC
skills/letta-ai/skills/largest-eigenval

largest-eigenval

SKILL.md

Largest Eigenvalue Optimization

Overview

This skill provides guidance for optimizing numerical computations that need to outperform standard library implementations like numpy or scipy. The primary focus is on finding the largest eigenvalue of small dense matrices, but the principles apply broadly to numerical optimization tasks.

When to Use This Skill

  • Optimizing eigenvalue computations to beat numpy.linalg.eig performance
  • Performance-critical numerical linear algebra on small dense matrices (2x2 to ~100x100)
  • Tasks requiring Cython extensions that call LAPACK directly
  • Any numerical optimization where Python wrapper overhead is the bottleneck

Critical First Step: Profile Before Optimizing

Before attempting any optimization, understand where time is actually spent.

  1. Profile the reference implementation to identify the bottleneck:

    • Is time spent in the algorithm itself (LAPACK routines)?
    • Or is time spent in Python wrapper overhead (input validation, memory allocation, type checking)?
  2. For small matrices, Python overhead often dominates:

    • numpy.linalg.eig is already calling optimized LAPACK (dgeev/zgeev)
    • The actual LAPACK computation is microseconds for small matrices
    • Wrapper overhead can be 2-10x the actual computation time
  3. Profile command example:

    import cProfile
    cProfile.run('for _ in range(10000): numpy.linalg.eig(matrix)')
    

Approach Decision Tree

Is the matrix small (< 100x100)?
├── YES: Python overhead likely dominates
│   ├── Consider: Cython calling LAPACK directly
│   ├── Consider: Reducing input validation overhead
│   └── AVOID: Iterative methods (power iteration, Arnoldi)
│         - Startup costs dominate for small matrices
│         - JIT compilation overhead (Numba) adds latency
└── NO: Algorithm choice matters more
    ├── For sparse matrices: scipy.sparse.linalg.eigs (Arnoldi)
    ├── For only dominant eigenvalue: Power iteration
    └── For full spectrum: Direct LAPACK via numpy

Recommended Approaches (In Order)

Approach 1: Cython + Direct LAPACK (Recommended for Small Matrices)

Why it works: Eliminates Python overhead while using the same optimized LAPACK routines.

Implementation steps:

  1. Create a Cython extension (.pyx file)
  2. Use cython.cdivision(True) and cython.boundscheck(False) for speed
  3. Call LAPACK dgeev/zgeev directly via scipy.linalg.cython_lapack
  4. Manage memory with numpy arrays, pass pointers to LAPACK
  5. Build with a setup.py using Cython.Build

Key optimizations:

  • Skip input validation (assume well-formed input)
  • Pre-allocate output arrays
  • Use typed memoryviews for array access
  • Minimize Python object creation

Approach 2: scipy.linalg.eig with check_finite=False

Why it helps: Skips the NaN/Inf checking that adds overhead.

Limitation: Still has more Python overhead than Cython approach.

from scipy.linalg import eig
eigenvalues, eigenvectors = eig(matrix, check_finite=False)

Approach 3: Specialized Algorithms (Only for Specific Cases)

Power Iteration: Only if you need just the dominant eigenvalue AND matrix is large.

  • Converges slowly for matrices with similar-magnitude eigenvalues
  • Each iteration is cheap, but many iterations needed
  • Overhead dominates for small matrices

scipy.sparse.linalg.eigs: Only for large sparse matrices.

  • Arnoldi iteration has significant startup cost
  • Designed for matrices too large to factorize directly
  • Overkill for dense matrices under 1000x1000

Common Pitfalls to Avoid

Pitfall 1: Using Iterative Methods for Small Matrices

Power iteration and Arnoldi methods have per-iteration overhead that compounds. For a 5x5 matrix, direct factorization is always faster.

Pitfall 2: Assuming Numba Will Be Faster

Numba's JIT compilation adds latency on first call. Even with caching, Numba functions have more overhead than pure C extensions for microsecond-scale operations.

Pitfall 3: Changing Algorithms When Overhead is the Problem

If numpy.linalg.eig spends 80% of time in Python wrappers and 20% in LAPACK, a "better algorithm" won't help. Reduce wrapper overhead instead.

Pitfall 4: Not Testing Complex Eigenvalues

Real matrices can have complex eigenvalues (e.g., rotation matrices). Always verify the solution handles complex results correctly.

Pitfall 5: Ignoring the "Largest" Definition

"Largest eigenvalue" typically means largest magnitude (absolute value), which may be negative or complex. Verify the dominance criterion.

Verification Strategy

Correctness Tests

  1. Eigenvalue equation: Verify A @ eigenvec ≈ eigenval * eigenvec (within numerical tolerance)
  2. Complex eigenvalues: Test with rotation matrices and skew-symmetric matrices
  3. Edge cases: 2x2 matrices, identity matrix, diagonal matrices

Dominance Tests

  1. Compare found eigenvalue against all eigenvalues from numpy.linalg.eig
  2. Verify it has the largest magnitude
  3. Test matrices where dominant eigenvalue is:
    • Positive real
    • Negative real
    • Complex
    • Part of a complex conjugate pair

Performance Tests

  1. Benchmark against reference implementation (numpy.linalg.eig)
  2. Test across all required matrix sizes
  3. Run multiple iterations to account for variance
  4. Verify consistent speedup, not just average speedup

Test Infrastructure

Create a comprehensive test suite once rather than ad-hoc verification:

def verify_eigenvalue(matrix, eigenval, eigenvec, rtol=1e-10):
    """Verify eigenvalue equation A @ v = lambda * v"""
    lhs = matrix @ eigenvec
    rhs = eigenval * eigenvec
    return np.allclose(lhs, rhs, rtol=rtol)

def verify_dominance(matrix, eigenval):
    """Verify this is the largest magnitude eigenvalue"""
    all_eigenvals = np.linalg.eig(matrix)[0]
    max_magnitude = np.max(np.abs(all_eigenvals))
    return np.isclose(np.abs(eigenval), max_magnitude)

Build and Deployment

Cython Build Setup

# setup.py
from setuptools import setup
from Cython.Build import cythonize
import numpy as np

setup(
    ext_modules=cythonize("eigen_fast.pyx"),
    include_dirs=[np.get_include()]
)

Graceful Fallback

Always provide a fallback to numpy if the optimized module fails to import:

try:
    from .eigen_fast import largest_eigenvalue
except ImportError:
    def largest_eigenvalue(matrix):
        eigenvalues, eigenvectors = np.linalg.eig(matrix)
        idx = np.argmax(np.abs(eigenvalues))
        return eigenvalues[idx], eigenvectors[:, idx]

Summary Checklist

Before implementing:

  • Profile the reference implementation
  • Identify whether bottleneck is algorithm or overhead
  • Choose approach based on matrix size and sparsity

During implementation:

  • For small dense matrices: Use Cython + direct LAPACK
  • Handle complex eigenvalues correctly
  • Implement graceful fallback

During verification:

  • Test eigenvalue equation correctness
  • Test dominance (largest magnitude)
  • Test complex eigenvalue cases
  • Benchmark performance across all matrix sizes
  • Verify consistent speedup
Weekly Installs
19
Repository
letta-ai/skills
First Seen
Jan 24, 2026
Installed on
claude-code14
gemini-cli13
codex13
opencode13
antigravity12
github-copilot10