Skip to content

added MPO.from_matrix() method#331

Merged
aaronleesander merged 32 commits intomunich-quantum-toolkit:mainfrom
lucello:bosehubbard
Feb 17, 2026
Merged

added MPO.from_matrix() method#331
aaronleesander merged 32 commits intomunich-quantum-toolkit:mainfrom
lucello:bosehubbard

Conversation

@lucello
Copy link
Contributor

@lucello lucello commented Feb 13, 2026

Description

A constructor MPO.from_matrix() is added to the MPO class in order to create custom MPOs from dense matrices.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds MPO.from_matrix() to factorize square dense matrices into a uniform-dimension MPO via an SVD left-to-right sweep with optional bond truncation; includes tests for round-trip conversions and minor reshape/test formatting tweaks.

Changes

Cohort / File(s) Summary
MPO Factorization Feature
src/mqt/yaqs/core/data_structures/networks.py
Added `@classmethod MPO.from_matrix(cls, mat: np.ndarray, d: int, max_bond: int
Test Coverage & Formatting
tests/core/data_structures/test_networks.py
Added test_from_matrix() to verify MPO.from_matrix() and round-trip to_matrix() for Bose–Hubbard and random matrices. Reflowed crandn signature and applied test formatting/line-wrapping changes (no semantic test behavior changes).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • added Bose-Hubbard Hamiltonian option #309 — Both PRs add new MPO-related classmethods in src/mqt/yaqs/core/data_structures/networks.py (this PR: from_matrix; #309: bose_hubbard), likely touching adjacent code paths.

Suggested labels

feature

Suggested reviewers

  • aaronleesander

Poem

🐰 I hop through matrices, splitting with care,
SVD carrots stacked in tidy rows to share.
From dense to MPO I stitch each little site,
Trim bonds soft and snug until they fit just right.
Tensors hum and sparkle in the moonlit night. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (17 files):

⚔️ .pre-commit-config.yaml (content)
⚔️ CHANGELOG.md (content)
⚔️ docs/index.md (content)
⚔️ pyproject.toml (content)
⚔️ src/mqt/yaqs/analog/analog_tjm.py (content)
⚔️ src/mqt/yaqs/core/data_structures/networks.py (content)
⚔️ src/mqt/yaqs/core/data_structures/noise_model.py (content)
⚔️ src/mqt/yaqs/core/data_structures/simulation_parameters.py (content)
⚔️ src/mqt/yaqs/core/methods/stochastic_process.py (content)
⚔️ src/mqt/yaqs/core/methods/tdvp.py (content)
⚔️ src/mqt/yaqs/digital/digital_tjm.py (content)
⚔️ src/mqt/yaqs/simulator.py (content)
⚔️ tests/analog/test_analog_tjm.py (content)
⚔️ tests/core/data_structures/test_networks.py (content)
⚔️ tests/core/data_structures/test_noise_model.py (content)
⚔️ tests/test_simulator.py (content)
⚔️ uv.lock (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
Description check ❓ Inconclusive The PR description is minimal but addresses the core change. However, it lacks substantial details such as motivation, context, dependencies, and the required checklist items. Expand description to include motivation/context for the new constructor, list any dependencies, and complete the provided checklist to confirm testing, documentation, and style compliance.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: addition of MPO.from_matrix() method and corresponding tests, matching the primary objectives of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 95.45% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@src/mqt/yaqs/core/data_structures/networks.py`:
- Line 1821: The call to np.asarray in the networks code sets order="F"
unnecessarily; remove the order argument so the line uses np.asarray(mat,
dtype=dtype) instead. Locate the assignment to M (M = np.asarray(mat,
dtype=dtype, order="F")) in the networks module and drop the order parameter to
avoid misleading Fortran-order implication while preserving semantics and
letting NumPy choose default memory layout.
- Around line 1784-1786: Replace the legacy typing names with modern built-in
generics and add a concrete dtype type: change the parameter annotation
"max_bond: Optional[int] = None" to "max_bond: int | None = None", keep "cutoff:
float = 0.0", and add a dtype annotation like "dtype: npt.DTypeLike =
np.complex128"; also change any "List[...]" usage at the later occurrence to
"list[...]" (the code around the parameter names max_bond/cutoff/dtype and the
list usage at the later site should be updated), and add "import numpy.typing as
npt" at the top of the module.
- Around line 1865-1872: The assignment mpo.physical_dimension currently sets a
tuple (d_in, d_out) but the class annotation and other constructors expect an
int; update the constructor that creates mpo (the cls() flow) to assign a single
integer to physical_dimension instead of a tuple—use d_in when d_in == d_out (or
use max(d_in, d_out) if you want a safe fallback for asymmetric inputs) and keep
the rest of the creation flow (tensors, length, mpo.check_if_valid_mpo())
unchanged; ensure references to physical_dimension elsewhere still treat it as
an int.

In `@tests/core/data_structures/test_networks.py`:
- Around line 389-407: The existing test only covers the happy path for
MPO.from_matrix with d_in==d_out and real random matrices; add three new cases:
(1) a test that calls MPO.from_matrix with an incompatible-shaped matrix (e.g.,
shape not equal to (d_in**L, d_out**L)) and asserts it raises ValueError
(exercise the error path in from_matrix), (2) a test that constructs a known
Hamiltonian (e.g., _ising_dense) and calls MPO.from_matrix with cutoff>0 to
verify lossy truncation still reconstructs within a tolerance using
mpo.to_matrix(), and (3) a test where d_in != d_out (non-square local operators)
to ensure from_matrix handles non-square local dimensions; also replace
np.random.rand usage with a complex random matrix (use a crandn equivalent or
combine RNG.standard_normal for real and imag) and use np.random.default_rng()
for reproducible randomness when generating random matrices.

@codecov
Copy link

codecov bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 98.21429% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/mqt/yaqs/core/data_structures/networks.py 98.2% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/mqt/yaqs/core/data_structures/networks.py`:
- Around line 1786-1802: The docstring for the method that "Factorize a dense
matrix into an MPO" should be converted from NumPy-style to the project's
Google-style to match other methods like hamiltonian, bose_hubbard, and
compress: replace the "Parameters" block with an "Args:" block listing mat
(np.ndarray), d (int), max_bond (int | None) — change Optional[int] to int |
None — and cutoff (float); then add a "Returns:" section that clearly documents
the returned MPO type and shape/meaning. Ensure parameter names mat, d,
max_bond, and cutoff appear exactly as in the signature and give a concise
description of the return (e.g., MPO object and inferred number of sites n).
- Around line 1809-1815: Add explicit validation for the base d and the inferred
chain length: check d first and raise clear errors for invalid values (if d <= 0
raise ValueError; if d == 1 then allow only rows == 1 and set n = 1, otherwise
raise ValueError). After computing n_float and n (using np.log(rows)/np.log(d)
as before), ensure n >= 1 and raise a ValueError if n == 0 (to prevent the later
“last site” reshape crash). Use the existing locals d, rows, n_float, and n to
implement these guards and update error messages to be explicit about the cause.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/mqt/yaqs/core/data_structures/networks.py`:
- Line 1811: Replace the Unicode multiplication sign U+00D7 in the docstrings
flagged by Ruff (RUF001/RUF002): change the occurrences of "1 × 1" (appearing in
the string If ``d == 1`` but the matrix is not ``1 × 1`` and the similar
instance at the later occurrence) to use ASCII "x" or "by" (e.g., "1 x 1" or "1
by 1") so the linter warnings go away; locate the exact text in
src/mqt/yaqs/core/data_structures/networks.py and update both occurrences
consistently.
- Around line 1895-1897: The statement "return mpo" is incorrectly dedented to
class/module level causing a SyntaxError; move the "return mpo" line to the same
indentation as the preceding "assert mpo.check_if_valid_mpo()" so it is inside
the method that initializes and returns the mpo (the function containing the
assert and mpo creation), ensuring the return belongs to that method and not the
class/module scope.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/mqt/yaqs/core/data_structures/networks.py`:
- Around line 1853-1859: The current _truncate(s: np.ndarray) uses cutoff > 0.0
so a default cutoff==0.0 keeps all singular values (including exact zeros);
either explicitly document that cutoff==0.0 means "no truncation, keep zeros" in
the function/docstring for _truncate (mention cutoff, s and max_bond) or, if the
intended behavior is to drop exact-zero singular values by default, change the
guard to treat zero as a threshold (e.g., use cutoff >= 0.0 when computing r =
max(int(np.sum(s > cutoff)), 1)) so exact-zero singular values are not counted
as meaningful bond dimensions; update tests/docs accordingly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/mqt/yaqs/core/data_structures/networks.py`:
- Around line 1800-1802: The docstring and implementation disagree: the code
uses the guard cutoff >= 0.0 and the filter s > cutoff, which with the default
cutoff==0.0 will discard exact-zero singular values (contrary to the docstring).
Fix by either (A) updating the docstring to state that cutoff=0.0 still drops
exact-zero singular values, or (B) change the guard logic so default behavior
keeps all values (replace cutoff >= 0.0 with cutoff > 0.0 so s > cutoff is only
applied when a positive cutoff is provided); update the same pattern around the
other occurrences referring to cutoff and the s > cutoff check.

@aaronleesander
Copy link
Member

@lucello Can you change the default cutoff for the singular values to 10^-12 rather than 0? Machines basically see anything under 10^-16 as 0 and the SVD can accumulate a lot of very small values like 10^-32 for example that are just errors from numerics. A setting slightly above 10^-16 is always a good idea to avoid this. Then this pull request is good to go and I'll merge it

@aaronleesander aaronleesander added the feature New feature or request label Feb 17, 2026
@aaronleesander aaronleesander merged commit e779755 into munich-quantum-toolkit:main Feb 17, 2026
15 checks passed
@aaronleesander aaronleesander changed the title Added MPO.from_matrix() and tests. added MPO.from_matrix() method Feb 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments