Conversions

Classical Register Conversion

-convert-cbit-to-memref

Lower CBit registers to memrefs

This pass explicitly lowers CBit register types and operations to memrefs. A zero-initialized register becomes an allocation followed by a loop that stores false to each element. An undefined register becomes an allocation only. The pass also converts function signatures, calls, returns, branches, and structural SCF operation types. It does not infer CBit registers from existing memrefs.

Internal Conversions

-qc-to-qco

Convert QC dialect to QCO dialect.

This pass converts all operations from the QC dialect to their equivalent operations in the QCO dialect. It handles the transformation of qubit references in QC to qubit values in QCO, ensuring that the semantics of quantum operations are preserved during the conversion process. Control flow must use structured SCF operations. Operations with block successors, including cf.br, cf.cond_br, and cf.switch, are diagnosed before conversion. Static qubit references are hoisted and coalesced when needed. General classical folding and common subexpression elimination are not guaranteed by this conversion; use the corresponding simplification passes when needed.

-qco-to-qc

Convert QCO dialect to QC dialect.

Convert QCO qubit values to QC references and QTensor registers to memrefs. Scalar function arguments become in-place references; each function must return its qubit arguments as a positional suffix. Other results remain explicit, including qubits created by a function.

Before rewriting, prove that quantum values yielded by modifiers, branches, and loops correspond positionally to their region arguments. Reject wire permutations and changes to quantum loop-state counts or types. Classical loop state can change type in scf.while. QTensor insertions can update register slots and become stores when the slot value changes.

Determine the module’s allocation mode before converting any function. Dynamic qubit sinks become deallocations; static qubit sinks are removed. Mixing static and dynamic allocation roots is unsupported.

jeff Conversions

-qco-to-jeff

Convert QCO operations to jeff operations

This pass converts all operations from the QCO dialect to their equivalent operations in the jeff dialect. It ensures that the returned module is a valid jeff module that can be serialized.

Defined single-block functions and their calls are preserved. Mutable classical-register arguments in helpers are not supported. Unitary calls inside quantum modifiers must be expanded with unroll-modifiers first.

Note that this pass is still in development as QCO and jeff do not have full feature parity yet.

qco.static is currently converted to jeff.qubit_alloc because jeff does not yet represent static (index-based) qubits. As the index is not preserved in jeff, it is not possible to round-tripping static qubits.

-jeff-to-qco

Convert jeff operations to QCO operations

This pass converts all operations from the jeff dialect to their equivalent operations in the QCO dialect. It ensures that the returned module is a valid QCO module.

Note that this pass is still in development as QCO and jeff do not have full feature parity yet.

Known limitations:

  • Only specific CustomOps are currently supported

  • Only specific PPROps are currently supported

  • Mutable classical-register arguments in helper functions are not supported

Function definitions and calls are preserved. Only the designated entry point remains public; helper functions become private. Calls remain generic func.call operations because jeff does not specify function unitarity.

QIR Conversions

-qc-to-qir-base

Lower the QC dialect to the LLVM dialect compliant with the QIR Base Profile 2.1

Lower supported QC operations to QIR Base Profile runtime calls and LLVM dialect operations. Run set-qir-attributes-and-metadata separately to attach QIR attributes and module flags.

Requirements:

  • Stores to returned CBit registers must share a block with their measurement, use an index available at measurement (or a constant), and have no intervening classical memory effects except stores to provably distinct constant indices of the same register.

  • Input is a valid module in the QC dialect.

  • The entry function must be marked with mqt.entry_point.

  • The input entry function must consist of a single block. Multi-block input functions are currently not supported.

  • The program must have straight-line control flow (i.e., Base Profile QIR).

  • A measured qubit must not be used by another quantum instruction, including another measurement. Gates on independent qubits may follow measurements in the input.

  • Explicit static qubit IDs cannot be mixed with qubit allocations.

  • Qubit-register loads require constant, in-bounds indices into statically sized allocations. Register aliases must be resolved before conversion.

Behavior:

  • Each QC quantum operation is replaced in place by its QIR call. After validating qubit usage in instruction order, the pass moves terminal measurements to the irreversible operations block.

  • The pass transforms the single-block entry function into four blocks to satisfy QIR Base Profile constraints: 0. Initialization block: Sets up the execution environment and performs required runtime initialization.

    1. Reversible operations block: Contains only void-returning calls to reversible quantum operations.

    2. Irreversible operations block: Contains terminal measurement calls (__quantum__qis__mz__body). Reset operations are not supported.

    3. Epilogue block: Records measurement results and returns from the entry function.

  • Blocks are connected via unconditional branches in the order listed above.

  • Non-quantum dialects are lowered via MLIR’s built-in conversions.

-qc-to-qir-adaptive

Lower the QC dialect to the LLVM dialect compliant with the QIR Adaptive Profile 2.1

Lower supported QC operations to QIR Adaptive Profile runtime calls and LLVM dialect operations. Run set-qir-attributes-and-metadata separately to attach QIR attributes and module flags.

Requirements:

  • Input is a valid module in the QC dialect.

  • The entry function must be marked with mqt.entry_point and have a single return.

  • Stores to returned CBit registers must share a block with their measurement, use an index available at measurement (or a constant), and have no intervening classical memory effects except stores to provably distinct constant indices of the same register.

Behavior:

  • Each QC quantum operation is replaced by a call to the corresponding QIR function in the LLVM dialect.

  • Operations from the scf dialect are lowered into the cf dialect before they are lowered to the LLVM dialect.

  • The pass extends the existing block structure of the entry function with an entry block and an epilogue block: 0. Initialization block: Sets up the execution environment and performs required runtime initialization.

    1. Epilogue block: Records measurement results and returns from the entry function. Any blocks in-between have no restrictions regarding their operations as long as they are supported.

  • Quantum releases retain their original control-flow positions.

  • Scalar and register results are dynamically allocated and released after output recording.

  • Measurement results may be used as classical values to drive conditional branches.

  • Non-quantum dialects are lowered via MLIR’s built-in conversions.