munich-quantum-toolkit / munich-quantum-toolkit/core

✨ Support register-aware qubit reuse for static QTensor allocations

Open
#1,990 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

c++ enhancement MLIR
Dominant language
C++
Stars
133
Forks
73
Avg merge
17h 34m
Merged PRs (30d)
261

Description

🤖 AI text below 🤖

Context

#1923 adds qubit reuse for individually allocated qco.alloc values. Qubits declared in statically sized registers instead lower through qtensor.alloc, qtensor.extract, qtensor.insert, and qtensor.dealloc, so the reuse pass currently leaves them untouched.

The closed #1125 proposed eliminating registers in the value-semantics dialect. This follow-up deliberately keeps the opposite boundary: retain the logical register in QCO IR and avoid globally scalarizing it merely to make reuse analysis easier.

Goal

Support qubit reuse for the common statically sized register case by:

  • reasoning about individual constant-index lanes of a QTensor;
  • reusing lanes whose quantum dataflow lifetimes do not overlap;
  • resetting reused lanes before their next logical lifetime;
  • reducing the amount of newly allocated register storage accordingly; and
  • preserving the logical tensor<Nx!qco.qubit> representation through the QCO optimization stage.

For a target register of width N with K reusable lanes, the intended resource effect is to allocate only the N - K fresh lanes while retaining an N-lane logical register for subsequent computation.

Required analysis

Generic forward-slice analysis is too coarse because every lane participates in the same tensor SSA chain. A register-aware analysis should build on qtensor::TensorIterator and track, for every statically known lane:

  • the matching constant-index extract/insert operations;
  • whether the lane is currently resident in or extracted from the tensor;
  • the scalar QCO wire between extraction and reinsertion;
  • interactions with other scalar wires that prevent reuse; and
  • the lane's allocation and final-use boundaries.

Different constant indices may commute through the tensor chain. A same-index insert, a dynamic index, ambiguous occupancy, or an unsupported region/CFG transition must invalidate the relevant proof rather than guessing.

Candidate implementation strategies

1. Equal-width whole-register reuse

Use this as the smallest first milestone. When every lane of a fully populated static register is dead before another equal-width register starts:

  1. consume the old tensor instead of deallocating it;
  2. extract, reset, and reinsert each lane;
  3. replace the later qtensor.alloc result with the reset tensor; and
  4. erase the redundant allocation/deallocation pair.

This can be expressed with the existing QTensor and QCO operations. It preserves the register abstraction and reduces two width-N allocation roots to one.

2. Static lane compaction

For partial reuse, compute a mapping from each logical target lane to either a reusable source lane or fresh storage. Allocate only the fresh portion, reset the reusable lanes, and reconstruct the full logical target register.

The existing qtensor.from_elements operation may be useful for reconstruction, but it is not currently supported by QCO-to-QC, QCO-to-Jeff, or the Mapping pass. An implementation choosing this route must add the required downstream handling and demonstrate that ownership and resource accounting remain correct.

This is the preferred general direction if it can be implemented without prematurely scalarizing the register's computation.

3. Mapping-time lifetime coloring

Alternatively, keep the QTensor structure unchanged and let Mapping assign the same physical qubit to non-overlapping logical lane lifetimes, inserting resets at lifetime boundaries.

This preserves the high-level IR longest and may avoid tensor reconstruction, but it couples reuse to placement and does not by itself reduce allocation roots in standalone optimized QCO. It should therefore be chosen only if structural compaction is substantially more invasive.

Suggested staging

  1. Introduce and test reusable static lane/occupancy analysis.
  2. Implement equal-static-width whole-register reuse.
  3. Extend Mapping to understand successive logical lifetimes on reused register lanes.
  4. Add partial static lane compaction and any required qtensor.from_elements consumer support.
  5. Consider dynamic indices and tensor-carried structured control flow separately.

Acceptance criteria

  • A QCO program with two noninteracting static register lifetimes requires fewer allocated qubits after reuse.
  • The whole-register case reduces two width-N allocation roots to one and preserves an N-lane QTensor for both logical lifetimes.
  • Partial static reuse reduces the total newly allocated width by exactly the number of safely reusable lanes.
  • Reused lanes are reset before their next lifetime; provably untouched allocation-zero lanes may canonicalize redundant resets away.
  • Interacting lanes, ambiguous aliases, partially populated tensors, dynamic indices, unsupported CFG shapes, and tensor-carried control flow are conservatively left unchanged in the initial implementation.
  • The resulting IR verifies and remains supported by QCO-to-QC, QCO-to-Jeff, Mapping, canonicalization, and register shrinking.
  • Tests cover whole-register reuse, partial reuse, non-reusable interactions, dynamic-index no-op behavior, incomplete occupancy, control-flow rejection, conversion, mapping, and OpenQASM input with qubit[N] declarations.
  • Pass documentation states the supported register shapes and conservative fallbacks.

Non-goals for the initial implementation

  • introducing a dedicated qtensor.reuse operation;
  • dynamic register extents or arbitrary dynamic lane aliases;
  • optimal/global lifetime coloring;
  • CFG-aware motion across basic blocks; or
  • tensor-carried structured control flow.

Relevant implementation areas

  • mlir/lib/Dialect/QCO/Transforms/Optimizations/ReuseQubits.cpp
  • mlir/lib/Dialect/QTensor/Utils/TensorIterator.cpp
  • mlir/lib/Dialect/QTensor/Transforms/ShrinkRegisters.cpp
  • mlir/lib/Dialect/QCO/IR/Operations/ResetOp.cpp
  • mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp
  • QCO-to-QC and QCO-to-Jeff conversions
  • QCO optimization, QTensor, conversion, Mapping, and compiler-pipeline tests

This issue should be picked up after #1923 is merged so the scalar-allocation reuse behavior and its tests form the baseline.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with mlir/lib/Dialect/QCO/Transforms/Optimizations/ReuseQubits.cpp and mlir/lib/Dialect/QTensor/Utils/TensorIterator.cpp, using #1923 and its tests as the baseline. Review the related ShrinkRegisters, ResetOp, Mapping, conversion, and compiler-pipeline areas before running the existing QCO optimization tests. Done means conservative static-lane analysis and reuse preserve the logical QTensor, reset reusable lanes, reduce allocation roots or fresh width, and pass the listed conversion and mapping coverage.

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
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.