✨ Add Quantum IPO Pass to mqt-cc - #1970
Closed
DRovara wants to merge 142 commits into
Closed
Conversation
…toolkit/core into mlir/measurement-lifting
Signed-off-by: Damian Rovara <93778306+DRovara@users.noreply.github.com>
This was referenced Aug 20, 2026
DRovara
added a commit
that referenced
this pull request
Aug 20, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 24, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 24, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 25, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 25, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 25, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
burgholzer
pushed a commit
that referenced
this pull request
Aug 25, 2026
…#2193) * ✨ Walk tensor chains across calls and compare untracked qubit tensors `TensorIterator` ends a tensor chain at `func.call` and `func.return` rather than aborting on them, so a chain reaching a call boundary can be walked. The tensor a call returns starts a life-chain of its own. `areModulesEquivalentWithPermutations` no longer assumes every qubit tensor belongs to an equivalence group. Tensors that do not descend from a `qtensor` allocation, such as function arguments, are compared through the regular SSA mapping, and the tensor threaded out of a `qtensor.extract` is now mapped alongside its qubit result. Both previously aborted inside the comparison instead of reporting a result. Extracted from #1970 as the first of a stack of smaller contributions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * 📝 Note the interprocedural optimization work in the changelog Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ✅ Cover walking a tensor chain backward from a call result The only branch this slice added that no existing test reached: a tensor a call hands back starts its own life-chain, so stepping back from it stops at the call rather than continuing into the tensor that was passed in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
burgholzer
pushed a commit
that referenced
this pull request
Aug 25, 2026
`quantum-argument-promotion` replaces a qubit-tensor argument by one qubit argument and one qubit result per element the callee actually touches, so untouched elements no longer cross the call boundary. Call sites are rewritten to extract before the call and re-insert afterwards. The rewrite lifts every extraction in front of the call and sinks every insertion behind it, which is only sound when the accesses do not depend on each other's order. Two shapes are therefore rejected: a slot the callee writes before reading it again, which would otherwise be served from the caller's original tensor and silently compute the wrong thing; and an insertion that belongs to no promoted slot, which used to survive the rewrite still using the tensor argument erased right after and abort on MLIR's `use_empty()` assertion. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
burgholzer
pushed a commit
that referenced
this pull request
Aug 25, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 26, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 26, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 26, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 27, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 27, 2026
`WireIterator` traverses `func.call` in both directions instead of stopping there, and `atWireStart()` reports whether a wire begins at the current operation, which a call result does only when the callee creates the qubit. Which result continues which operand is derived by threading the callee's arguments through its body rather than assumed to be positional, so a callee that hands its qubits back in a different order is followed correctly. The derivation is cached per callee by `CallQubitMapping`, with `CallTensorMapping` providing the same for qubit tensors. External, recursive and multi-block callees fall back to the positional pairing. Also fixes two defects this surfaced: threading asked for a body's terminator before checking that one exists, which trips on a function still being built, and `qtensor.from_elements` was not treated as a wire sink even though it consumes qubits into a tensor exactly like `qtensor.insert`. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 27, 2026
`startFunction`/`endFunction` open and close a function next to `main`, and `call` emits a `func.call` to one. Test programs spanning several functions can now be built with the builder instead of hand-written MLIR. Linear values are isolated per function: `startFunction` sets aside the caller's tracked qubits and tensors and restores them in `endFunction`, so an outer value used inside a callee is rejected rather than silently threaded, and anything the callee leaves behind is reported as a leak. At a call, each linear operand is paired with the result that continues it, derived from the callee through `CallQubitMapping` and `CallTensorMapping` rather than by position. An operand the callee keeps counts as consumed, and a result no operand flows into as freshly created. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 27, 2026
`quantum-context-sensitive-specialization` redirects a `func.call` to a
specialized copy of its callee whenever the call site pins something down: a
qubit argument known to be in |0>, letting operations that fix |0> be dropped;
one known to be in |+>, letting an `x` be dropped; or a compile-time constant
rotation angle from a small distinguished set, which is folded into the copy.
Copies are shared between call sites with the same context, and a callee left
without callers is erased — but only when this pass created that situation, so
unrelated unused functions are left alone. Specializations are private, so
optimizing a public callee does not export the generated symbol.
`IPOUtils.{h,cpp}` holds the two helpers the interprocedural passes share.
The tests are scheduled on this pass alone rather than on a pipeline, so a
regression here cannot be masked by a later pass. `IPOTestFixture.h` carries the
fixture the sibling passes will reuse.
Extracted from #1970.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 27, 2026
`quantum-argument-promotion` replaces a qubit-tensor argument by one qubit argument and one qubit result per element the callee actually touches, so untouched elements no longer cross the call boundary. Call sites are rewritten to extract before the call and re-insert afterwards. The rewrite lifts every extraction in front of the call and sinks every insertion behind it, which is only sound when the accesses do not depend on each other's order. Two shapes are therefore rejected: a slot the callee writes before reading it again, which would otherwise be served from the caller's original tensor and silently compute the wrong thing; and an insertion that belongs to no promoted slot, which used to survive the rewrite still using the tensor argument erased right after and abort on MLIR's `use_empty()` assertion. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 27, 2026
`quantum-auxiliary-qubit-hoisting` turns a qubit a callee allocates and releases itself into an extra argument and an extra result, handed back reset, so the caller owns the allocation and can reuse one qubit across several calls. Externally visible functions, declarations and recursive functions are left alone. Candidates are visited callees first, in a post-order over the call graph. Hoisting out of a callee puts an allocation into each of its callers, which may itself be hoistable; processing in module order used to strand that allocation wherever the declarations happened to sit, so the same call graph gave different results depending on declaration order. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 27, 2026
`quantum-function-boundary-commutation` drops a self-inverse gate applied right before a call when the callee starts by applying the same gate to that argument. The caller-side gate is erased and the call is redirected to a copy of the callee without the callee-side one. Copies are cached per callee *and* parameter index. The gate removed inside the copy belongs to one specific argument, so sharing a copy between parameters would drop a gate the second call site never had. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 27, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 28, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DRovara
added a commit
that referenced
this pull request
Aug 28, 2026
`populateQuantumIPOPipeline` runs specialization, argument promotion, auxiliary qubit hoisting and boundary commutation in the order they are meant to compose, and registers them as the `quantum-ipo` pipeline. Commutation runs twice because it can expose further cancellations. The passes stay individually registered, so a caller assembling its own pipeline can pick only the ones it wants. `test_qco_quantum_ipo.cpp` keeps only the scenarios that need more than one pass. Everything about a single pass lives in that pass's own suite, scheduled on the pass alone. Extracted from #1970. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Member
|
I will close this one for now as well, as individual PRs have been set up. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR introduces the IPO passes from the corresponding paper to
mqt-cc.It includes:
AI Notice: Gemini 3.1 Pro and Claude Opus 5.0 were used for the generation of some code.
Checklist
If PR contains AI-assisted content:
🤖 *AI text below* 🤖(titles are exempt).