Skip to content

Subspace: project with conj(basis), expand with basis.T, so complex signals round-trip - #51

Merged
mcencini merged 2 commits into
mainfrom
claude/project-thread-dvys3p-subspace
Nov 1, 2026
Merged

mcencini merged 2 commits into
mainfrom
claude/project-thread-dvys3p-subspace

Conversation

@mcencini

@mcencini mcencini commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Requested by Matteo · project thread

What changed

Subspace.fit keeps the left singular vectors B of the signals laid out as columns. The two maps now match that:

  • project(s) = s @ B.conj(), the inner products ⟨b_k, s⟩
  • expand(c) = c @ B.mT, so expand(project(s)) == s for any signal in the span, complex included
  • modes = B.mH (resolve_conj'd), which is mri-nufft's MRISubspace layout. MRISubspace expands with conj(modes) and projects with modes, which are exactly expand and project.
  • BART's pics -B and bartorch's basis= contract without the conjugate, so they take basis.mT. The docstring and docs/explanations/model-based-reconstruction.md say so.

Why

Before this change, project was s @ B and expand was c @ B.mH. Those two are adjoints of each other, so they agreed with mri-nufft. But they project onto the conjugate of the signals' span. On random complex signals that lie in a rank-3 span, the round trip had 96 % error. Real dictionaries, and dictionaries whose atoms share one global phase, hid this. A dictionary whose phase changes along the train does not, for example one with off-resonance or RF phase cycling. Found while moving mri2qmap's real-coefficient basis rotation upstream (mri2qmap-bartorch).

Validation

  • New tests/estimators/test_subspace.py::test_complex_signals_of_the_span_round_trip fails before the change and passes after it. It also checks the modes / mri-nufft relations.
  • tests/operator/test_subspace_route.py now runs both a real basis and an off-resonant complex one against mri-nufft's MRISubspace (op and adj_op). All 5+2 pass. Locally I had to stub mrinufft.check_backend, because the installed mri-nufft lacks it, and install finufft.
  • pytest tests/estimators tests/operator tests/test_docstring_defaults.py: 285 passed, 32 skipped (CUDA).
  • ruff format / ruff check: clean.
  • Not run: the full suite, the docs build, and a GPU.

Documentation impact

Subspace.project, expand and modes docstrings, the model-based reconstruction explanation, and a CHANGELOG Unreleased/Fixed entry. For callers with complex, phase-varying dictionaries, coefficients change by a conjugation of the basis. That is the fix.

Checklist

  • A new test covers the change and fails before it.
  • ruff is clean.
  • numpydoc docstrings.
  • Comments describe the current code (the history is in the CHANGELOG only).
  • Kernels are not touched.
  • Docs build not run.

Generated by Claude Code

project is signals @ basis.conj() and expand is coefficients @ basis.mT, so a
complex signal of the span comes back unchanged; modes is basis.mH, the
layout mri-nufft's MRISubspace expands with its conjugate. A complex
dictionary whose atoms do not share one phase was projected onto the
conjugate of its span. Real dictionaries are unaffected.

The subspace-route tests now also run an off-resonant, complex basis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MFB6QM5LeyLiZBruB754ux
@mcencini mcencini self-assigned this Nov 1, 2026
…-dvys3p-subspace

# Conflicts:
#	CHANGELOG.md
@mcencini
mcencini merged commit 339d0be into main Nov 1, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants