Skip to content

✨ Add complex eigendecomposition for QCO matrices#1814

Open
simon1hofmann wants to merge 11 commits into
mainfrom
feat/eispack-eigendecomposition
Open

✨ Add complex eigendecomposition for QCO matrices#1814
simon1hofmann wants to merge 11 commits into
mainfrom
feat/eispack-eigendecomposition

Conversation

@simon1hofmann

Copy link
Copy Markdown
Contributor

Description

Adds self-contained eigendecomposition for QCO matrix types without an Eigen dependency, as groundwork for unitary matrix powers (U^α = V diag(λ^α) V†).

Fixes #1811

Checklist

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

If PR contains AI-assisted content:

  • I have disclosed the use of AI tools in the PR description as per our AI Usage Guidelines.
  • AI-assisted commits include an Assisted-by: [Model Name] via [Tool Name] footer.
  • I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

@simon1hofmann simon1hofmann changed the title ✨ Add EISPACK-based complex eigendecomposition for QCO matrices ✨ Add complex eigendecomposition for QCO matrices Jun 26, 2026
@simon1hofmann simon1hofmann self-assigned this Jun 26, 2026
@simon1hofmann simon1hofmann added enhancement Improvement of existing feature c++ Anything related to C++ code MLIR Anything related to MLIR labels Jun 26, 2026
@simon1hofmann simon1hofmann added this to the MLIR Support milestone Jun 26, 2026
@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.79012% with 26 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp 97.1% 22 Missing ⚠️
mlir/lib/Dialect/QCO/Utils/Matrix.cpp 90.4% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

@simon1hofmann

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 701cee8d-541a-422a-a423-b67334d0b074

📥 Commits

Reviewing files that changed from the base of the PR and between 9de67d4 and 3c01a5c.

📒 Files selected for processing (4)
  • mlir/lib/Dialect/QCO/Utils/CMakeLists.txt
  • mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp
  • mlir/lib/Dialect/QCO/Utils/Matrix.cpp
  • mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added complex eigen-decomposition APIs for fixed-size (1×1/2×2/4×4) and dynamic square matrices, plus symmetric real eigen-decomposition for 4×4.
    • Exposed results via eigenvalues and eigenvectors, with non-converging cases reported as optional/empty.
  • Bug Fixes
    • Strengthened matrix bounds checking and index validation to prevent out-of-range access.
    • Improved numerical robustness and consistency of eigenpair computations across fixed-size vs dynamic paths.
  • Tests
    • Expanded unit tests to validate eigen-decomposition correctness, convergence behavior, and cross-check specialized vs dynamic results.

Walkthrough

This PR adds eigendecomposition support for QCO matrices. It introduces public complex and symmetric eigen APIs, implements fixed-size and dynamic solvers, routes matrix methods to those helpers, updates build wiring, and expands unit tests.

Changes

QCO eigendecomposition support

Layer / File(s) Summary
Public contracts
mlir/include/mlir/Dialect/QCO/Utils/Matrix.h, mlir/include/mlir/Dialect/QCO/Utils/Eigensolver.h
Adds the complex and symmetric eigen result structs, complexEigen() APIs, the SupportedMatrix concept, and the new public solver declarations.
Real symmetric 4x4 solver
mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp
Implements 4x4 symmetric tridiagonalization, QL iteration, and the decomposeSymmetricEigen4 overloads for arrays and Matrix4x4.
Complex solver numerics
mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp
Adds the complex numeric helpers, EispackMatrixView, stable complex arithmetic, Hessenberg reduction, and QR iteration.
Complex solver wrappers
mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp
Assembles fixed-size results, adds the 1x1, 2x2, and 4x4 decomposition paths, lifts fixed results into dynamic storage, and implements the dynamic square solver.
Matrix indexing and bounds
mlir/lib/Dialect/QCO/Utils/Matrix.cpp
Switches fixed-size and dynamic element access to checked flat indices, tightens row/column assertions, and updates supporting helpers.
Solver delegation and build wiring
mlir/lib/Dialect/QCO/Utils/Matrix.cpp, mlir/lib/Dialect/QCO/Utils/CMakeLists.txt
Adds the solver include and source wiring, routes eigen methods through the shared helpers, and updates DynamicMatrix::complexEigen().
Tests and fixtures
mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp
Adds complex-eigen helpers and coverage, switches concept checks to SupportedMatrix, and updates several matrix fixtures and parameterized cases.

Sequence Diagram(s)

sequenceDiagram
  participant DynamicMatrix
  participant Matrix2x2
  participant Matrix4x4
  participant Eigensolver
  alt dim == 1
    DynamicMatrix->>Eigensolver: decomposeComplexEigen1x1(*this)
    Eigensolver-->>DynamicMatrix: std::optional<ComplexEigen>
  else dim == 2
    DynamicMatrix->>Matrix2x2: assignFrom(*this)
    Matrix2x2->>Eigensolver: decomposeComplexEigen2(matrix)
    Eigensolver-->>Matrix2x2: std::optional<ComplexEigen2>
    Matrix2x2->>Eigensolver: fromComplexEigen(eigen2)
    Eigensolver-->>DynamicMatrix: ComplexEigen
  else dim == 4
    DynamicMatrix->>Matrix4x4: assignFrom(*this)
    Matrix4x4->>Eigensolver: decomposeComplexEigen4(matrix)
    Eigensolver-->>Matrix4x4: std::optional<ComplexEigen4>
    Matrix4x4->>Eigensolver: fromComplexEigen(eigen4)
    Eigensolver-->>DynamicMatrix: ComplexEigen
  else other square size
    DynamicMatrix->>Eigensolver: decomposeComplexEigenDynamic(*this)
    Eigensolver-->>DynamicMatrix: std::optional<ComplexEigen>
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

feature

Suggested reviewers

  • burgholzer
  • denialhaag

Poem

A bunny found eigenpairs at dawn,
In 4x4 hops and dynamic lawn. 🐇
The matrices twirled, then settled down,
With complex roots in a velvet crown,
And quiet traces wore a grin.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: adding eigendecomposition support for QCO matrices.
Description check ✅ Passed The description includes a summary, the fixed issue, and the required checklist section.
Linked Issues check ✅ Passed The changes satisfy #1811 by adding eigenvalue and eigenvector support for QCO matrices.
Out of Scope Changes check ✅ Passed No clearly unrelated changes are introduced; the broader solver, tests, and build updates support the eigendecomposition work.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/eispack-eigendecomposition

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mlir/lib/Dialect/QCO/Utils/CMakeLists.txt`:
- Around line 20-24: The public header set for MLIRQCOMatrix is missing
Eigensolver.h even though Eigensolver.cpp now belongs to that target, which
leaves the declarations owned by a different target. Update the target_sources
entry for MLIRQCOMatrix to export Eigensolver.h alongside Matrix.h, and adjust
the later MLIRQCOUtils header glob/filter so Eigensolver.h is excluded there and
not duplicated under the wrong target. Use the existing target_sources,
MLIRQCOMatrix, and MLIRQCOUtils sections in this CMakeLists.txt to make the
ownership consistent.

In `@mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp`:
- Around line 187-189: The QL non-convergence path in symmetricTql2_4 only uses
an assert, so non-asserting builds can keep looping past the convergence limit.
Update this branch to terminate deterministically by returning a convergence
failure status or otherwise failing out immediately from symmetricTql2_4 when j
reaches the limit, and make sure the caller of this eigensolver path handles
that failure instead of continuing with invalid eigenpairs.
- Around line 697-702: Guard the exceptional shift in
Eigensolver::computeQrIteration so the 10/20-iteration branch does not read
hessenbergReal at activeEigenIndex - 2 when the active QR window has collapsed
to a 2x2 block. Add a condition around the shift calculation using
activeEigenIndex, activeEigenIndexMinus1, and rowLow so the second term is only
accessed when the column index is non-negative, and otherwise skip or adjust the
exceptional shift path to avoid the out-of-bounds access.
- Around line 1097-1104: The 2x2 diagonal fast-path in Eigensolver.cpp only
returns one basis vector twice when both eigenvalues match, which makes the
eigenvector matrix singular. Update the branch that handles std::abs(b) <=
MATRIX_TOLERANCE and std::abs(c) <= MATRIX_TOLERANCE so it returns a full
orthonormal basis for the diagonal case: keep the current behavior when lambda
matches a, but when a == d and both eigenvalues are equal, choose distinct basis
vectors for the two columns instead of always using [1, 0]. Make sure the logic
in the eigenvector construction path produces linearly independent vectors for
both eigenvalues.

In `@mlir/lib/Dialect/QCO/Utils/Matrix.cpp`:
- Around line 578-579: The invalid two-qubit reorder path in the
Matrix::reorderForQubits logic is currently only enforced by an assert, so it
becomes a silent fallthrough in release builds. Update the invalid-index
handling to use a fatal non-returning invariant failure, consistent with the
neighboring invalid-index branch, so public calls like reorderForQubits(0, 0)
cannot continue past the check. Use the existing Matrix::reorderForQubits and
related invalid-index handling as the anchor points when making the change.

In `@mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp`:
- Around line 67-70: The eigenvalue checks in the unitary matrix tests are
comparing only magnitudes, which can hide conjugated or phase-shifted results.
Update the assertions in the fixed-vs-dynamic comparison loops to compare the
complex eigenvalues directly in the relevant test cases, using the existing
fixed and dynamic solver outputs from the test helper instead of std::abs on
fixed->eigenvalues and dynamic->eigenvalues. This should be applied in both the
2x2 and 4x4 paths so the dynamic solver is verified against the lifted fixed
result itself.
- Around line 161-184: The eigen decomposition helpers currently validate
residuals without first confirming the solver returned a full square
decomposition. Update expectComplexEigen and expectGeneralComplexEigen to assert
that ComplexEigen contains exactly matrix.rows() eigenvalues and that the
eigenvector/eigenpair matrix has the expected matching shape before checking
residuals or traces, using the existing complexEigen(), eigenvalues, and
eigenpairsSatisfy/maxEigenpairResidual helpers.
- Line 929: The deterministic seed initialization in test_unitary_matrix.cpp
should match the existing lint suppression pattern used elsewhere in this test
file. Update the std::mt19937 rng initialization in the unitary matrix test
helper to include the same NOLINT(cert-msc51-cpp) suppression used by the other
deterministic seeds (for example, the ones in the nearby test cases) so
clang-tidy stays consistent across these test utilities.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2937e65c-76ad-4598-8d4a-28f604ff67ca

📥 Commits

Reviewing files that changed from the base of the PR and between 95e7e05 and 9de67d4.

📒 Files selected for processing (6)
  • mlir/include/mlir/Dialect/QCO/Utils/Eigensolver.h
  • mlir/include/mlir/Dialect/QCO/Utils/Matrix.h
  • mlir/lib/Dialect/QCO/Utils/CMakeLists.txt
  • mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp
  • mlir/lib/Dialect/QCO/Utils/Matrix.cpp
  • mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp

Comment thread mlir/lib/Dialect/QCO/Utils/CMakeLists.txt Outdated
Comment thread mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp
Comment thread mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp
Comment thread mlir/lib/Dialect/QCO/Utils/Eigensolver.cpp Outdated
Comment thread mlir/lib/Dialect/QCO/Utils/Matrix.cpp Outdated
Comment thread mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp
Comment thread mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp
Comment thread mlir/unittests/Dialect/QCO/Utils/test_unitary_matrix.cpp Outdated
@simon1hofmann

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@simon1hofmann simon1hofmann marked this pull request as ready for review June 29, 2026 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Anything related to C++ code enhancement Improvement of existing feature MLIR Anything related to MLIR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Add eigenvalue and eigenvector support to QCO Matrix

1 participant