munich-quantum-toolkit / munich-quantum-toolkit/core
✨ Support quantum-register function arguments in QC and QCO
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 133
- Forks
- 73
- Avg merge
- 17h 34m
- Merged PRs (30d)
- 261
Description
🤖 AI text below 🤖
Problem Statement
QC and QCO already represent qubit registers, but their reusable-function paths do not share an ownership contract:
QCOProgramBuilder::createFunctiontracksQTensorarguments, whilecallrejects every quantum-tensor operand.- QC→QCO accepts only directly allocated qubit memrefs and rejects function register arguments.
- QCO→QC converts qubit tensors to memrefs but removes pass-through results only for scalar-qubit arguments.
- Hand-written invalid IR can reorder same-typed register results unless a shared provenance check owns the invariant.
Frontend-specific workarounds would duplicate this logic and can reconnect the wrong linear wires.
Proposed Solution
Add one function-call contract for quantum arguments.
Types and ownership
- QC register arguments are borrowed rank-one
memref<Nx!qc.qubit>ormemref<?x!qc.qubit>values. Require the default memory space and identity layout. - QCO register arguments are linear rank-one
tensor<Nx!qco.qubit>ortensor<?x!qco.qubit>values. - Use ordinary
func.callfor every register-taking function. Such functions must not carrymqt.unitaryand must not useqc.callorqco.call. - A QCO function returns ordinary results first. It then returns one pass-through quantum result for each scalar-qubit or qubit-register argument, in original argument order and with the matching type.
- QC does not return borrowed quantum arguments. QCO→QC removes the matching suffix. Newly allocated quantum results before the suffix remain owned results.
Supported provenance
- Accept whole registers produced by a direct allocation or received as an enclosing function argument.
- Reject unranked registers, non-identity layouts, non-default memory spaces, views, subviews, casts, and other aliases whose identity is not proved.
- Reject a repeated register and a call that passes both a whole register and a scalar qubit loaded from it.
- Initially require direct calls to complete, non-recursive, single-outer-block definitions. Reject declarations and unresolved callees.
Implementation direction
- Extend
FunctionUtilswith tensor-argument provenance besidetraceQubitArgument. ReuseTensorIteratorand cross nestedfunc.callonly through the positional contract. Detect cycles and fail closed. - Extend
QCOProgramBuilderto consume and rebind tensor operands. Reinsert live extracted elements before a call and transfer each register identity to its matching result. - Extend QC→QCO register collection to seed function arguments with register identities, pass the current tensor state at calls, and append updated tensors at returns.
- Extend QCO→QC validation to check and remove the complete scalar/register pass-through suffix.
- Keep
verifyLinearityas the owner of one-use checking. Do not add a second linearity framework or frontend-specific call mappings.
Likely implementation areas:
mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cppmlir/include/mlir/Dialect/QCO/Utils/FunctionUtils.hmlir/lib/Dialect/QCO/Utils/FunctionUtils.cppmlir/lib/Conversion/QCToQCO/QCToQCO.cppmlir/lib/Conversion/QCOToQC/QCOToQC.cpp- Corresponding builder, utility, and conversion tests
Acceptance criteria
- QC→QCO→QC succeeds for static and dynamic rank-one register arguments.
- Tests cover two registers, mixed scalar-qubit/register arguments, nested calls, classical results, and a newly allocated register result.
- Negative tests cover repeated or overlapping operands, missing or reordered pass-through results, declarations, recursion, multi-block definitions, unranked/view operands, and attributes on erased pass-through results.
- Every successful QCO result passes
qco::verifyLinearity. - Builder tests prove consumption and rebinding: an input tensor becomes invalid at the call and only its matching result remains live.
- A non-inlined QCO helper round trips through QCO→jeff→serialization→jeff→QCO with both static and dynamic
quregsignatures and calls. - Valid input never reaches an assertion or crash; unsupported input receives a targeted diagnostic.
Dependencies
Parent feature: #2427
This is the foundational pull request. The OpenQASM and QIR sub-issues depend on it.
Non-goals
Register modifiers; indirect calls; external register declarations; recursive call graphs; arbitrary memref layouts, memory spaces, slices, or aliases; target-specific lowering beyond the jeff regression.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with FunctionUtils.h/.cpp and the existing traceQubitArgument logic, then inspect QCOProgramBuilder.cpp, QCToQCO.cpp, and QCOToQC.cpp. Run the corresponding builder, utility, and conversion tests to understand current call and ownership behavior. Done means supported static and dynamic register arguments round-trip with linearity preserved, while unsupported provenance and call shapes receive targeted diagnostics without crashes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100