‘qco’ Dialect¶
The QCO (value semantics) dialect for quantum computing.
The QCO dialect uses **value semantics** where quantum operations
consume input qubits and produce new output values, following the
SSA model. Dependencies between quantum operations are explicit in
the use-def chains.
The name "QCO" stands for "Quantum Circuit Optimization."
In a program, dynamic qubit and qubit-tensor allocations must be in
the entry block of the `mqt.entry_point` function. Helper functions
receive quantum resources as arguments. The MQT entry-point verifier
checks this rule across the program.
Register slots retain their qubit identities, and quantum operands must denote disjoint resources. See the shared quantum resource contract for helper boundaries, dynamic-index preconditions, and semantic round trips.
Example:
```mlir
%q_out = qco.h %q_in // Consumes %q_in, produces %q_out
%q0_out, %q1_out = qco.swap %q0_in, %q1_in // Consumes inputs, produces outputs
```
Operations¶
qco.alloc (qco::AllocOp)¶
Allocate a qubit dynamically
Syntax:
operation ::= `qco.alloc` attr-dict `:` type($result)
Allocates a new qubit dynamically and returns an SSA value representing it. The qubit is initialized to the |0⟩ state.
Example:
%q = qco.alloc : !qco.qubit
Interfaces: InferTypeOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{MemoryEffects::Allocate on ::mlir::SideEffects::DefaultResource}
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.barrier (qco::BarrierOp)¶
Apply a barrier operation to a set of qubits
Syntax:
operation ::= `qco.barrier` $qubits_in attr-dict `:` type($qubits_in)
Marks a barrier and transfers ownership of the input qubits to the results without changing their quantum state.
Example:
%q_out = qco.barrier %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
variadic of QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
variadic of QCO qubit value type |
qco.call (qco::CallOp)¶
Call a unitary QCO function
Syntax:
operation ::= `qco.call` $callee `(` $operands `)` attr-dict `:`
functional-type($operands, $qubits_out)
Calls a private func.func marked with mqt.unitary. Parameters precede
qubit operands. Each qubit result continues the corresponding qubit input.
Calls conservatively carry a write effect and are not speculatable.
Module-level analyses may prove more precise effects for individual
optimizations. Generic effect queries do not inspect other function bodies.
Example:
%q1 = qco.call @rotate(%theta, %q0)
: (f64, !qco.qubit) -> !qco.qubit
Interfaces: ArgAndResultAttrsOpInterface, CallOpInterface, MemoryEffectOpInterface (MemoryEffectOpInterface), SymbolUserOpInterface, UnitaryOpInterface
Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}
Attributes:¶
| Attribute | MLIR Type | Description |
|---|---|---|
callee | ::mlir::FlatSymbolRefAttr | flat symbol reference attribute |
arg_attrs | ::mlir::ArrayAttr | Array of dictionary attributes |
res_attrs | ::mlir::ArrayAttr | Array of dictionary attributes |
Operands:¶
Operand |
Description |
|---|---|
|
variadic of any non-token type |
Results:¶
Result |
Description |
|---|---|
|
variadic of QCO qubit value type |
qco.ctrl (qco::CtrlOp)¶
Apply a control modifier to a collection of gates
Syntax:
operation ::= `qco.ctrl` `(` $controls_in `)`
`targets`
custom<TargetAliasing>($region, $targets_in)
attr-dict `:`
`(` `{` type($controls_out) `}` ( `,` `{` type($targets_out)^ `}` )? `)`
A modifier operation that adds control qubits to the gates defined in its body region. The controlled operation applies the controlled gates only when all control qubits are in the \(|1\rangle\) state.
The operation takes a variadic number of control and target qubits as inputs and produces corresponding output qubits. Control qubits are not modified by the operation and simply pass through to the outputs.
The body region may contain unitary operations and memory-effect-free classical operations without regions.
Parameter computation is eager, as specified by the mqt.unitary contract; it need not be speculatable.
Classical SSA values may be captured from above, but every qubit used
in the body must be passed through a modifier operand and accessed via
its aliased block argument.
Classical memory access and non-unitary region operations are not allowed.
Unitary operations retain their required effects, including global phase.
Example:
%res_ctrl, %res_tgt:2 = qco.ctrl(%ctrl) targets(%a0 = %q0, %a1 = %q1) {
%a0_1, %a1_1 = qco.swap %a0, %a1 : !qco.qubit, !qco.qubit
qco.yield %a0_1, %a1_1 : !qco.qubit, !qco.qubit
} : ({!qco.qubit}, {!qco.qubit, !qco.qubit})
Traits: AttrSizedOperandSegments, AttrSizedResultSegments, RecursiveMemoryEffects, RecursivelySpeculatableImplTrait, SameOperandsAndResultShape, SingleBlockImplicitTerminator<YieldOp>, SingleBlock
Interfaces: ConditionallySpeculatable, UnitaryOpInterface
Operands:¶
Operand |
Description |
|---|---|
|
variadic of QCO qubit value type |
|
variadic of QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
variadic of QCO qubit value type |
|
variadic of QCO qubit value type |
qco.dcx (qco::DCXOp)¶
Apply a DCX gate to two qubits
Syntax:
operation ::= `qco.dcx` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies a DCX gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.dcx %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.ecr (qco::ECROp)¶
Apply an ECR gate to two qubits
Syntax:
operation ::= `qco.ecr` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies an ECR gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.ecr %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.gphase (qco::GPhaseOp)¶
Apply a global phase to the state
Syntax:
operation ::= `qco.gphase` `(` $theta `)` attr-dict
Applies a global phase to the state.
Constant angles must be finite and have magnitude at most 10000 radians. Dynamic angles have the same runtime precondition.
Example:
qco.gphase(%theta)
Traits: ZeroTargetOneParameter
Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{MemoryEffects::Write on ::mlir::SideEffects::DefaultResource}
Operands:¶
Operand |
Description |
|---|---|
|
64-bit float |
qco.h (qco::HOp)¶
Apply a H gate to a qubit
Syntax:
operation ::= `qco.h` $qubit_in attr-dict `:` type($qubit_in)
Applies a H gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.h %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.id (qco::IdOp)¶
Apply an Id gate to a qubit
Syntax:
operation ::= `qco.id` $qubit_in attr-dict `:` type($qubit_in)
Applies an Id gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.id %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.if (qco::IfOp)¶
If-then-else operation with classical and linear results
The qco.if operation is an if-then-else construct similar to the standard scf.if operation.
In addition to the condition, the operation takes a variadic number of qubits and qtensors as inputs that are
required in the bodies of both branches. These values are passed down to the individual regions
as block arguments. The operation may additionally return ordinary
classical SSA values. Classical results precede the linear results.
The number and types of the linear results must match the input qubit
and qtensor values.
Example:
%flag, %result = qco.if %condition args(%arg0 = %q0)
-> (i1, !qco.qubit) {
%q1 = qco.h %arg0 : !qco.qubit
qco.yield %true, %q1 : i1, !qco.qubit
} else args(%arg0 = %q0) {
qco.yield %false, %arg0 : i1, !qco.qubit
}
Traits: AttrSizedResultSegments, RecursiveMemoryEffects, RecursivelySpeculatableImplTrait, SingleBlockImplicitTerminator<YieldOp>, SingleBlock
Interfaces: ConditionallySpeculatable, RegionBranchOpInterface
Operands:¶
Operand |
Description |
|---|---|
|
1-bit signless integer |
|
variadic of QCO qubit value type or 1D tensor of QCO qubit value type values |
Results:¶
Result |
Description |
|---|---|
|
variadic of any non-token type |
|
variadic of QCO qubit value type or 1D tensor of QCO qubit value type values |
qco.index_switch (qco::IndexSwitchOp)¶
Index-based switch operation with classical and linear results
The qco.index_switch operation provides multi-way branching based on an index
value, analogous to scf.index_switch. In addition to the index, the operation
takes a variadic number of qubits and qtensors as inputs that are required in all
case branches. These values are passed down to each case region as block arguments.
The operation may additionally return ordinary classical SSA values.
Classical results precede the linear results. The number and types of
the linear results must match the input qubit and qtensor values.
The operation has one region for each case value plus a default region. Each region
must terminate with a qco.yield operation that yields values matching the operation’s
result types.
Example:
%flag, %result = qco.index_switch %index -> (i1, !qco.qubit)
case 0 args(%arg0 = %q0) {
%q1 = qco.h %arg0 : !qco.qubit
qco.yield %true, %q1 : i1, !qco.qubit
}
case 1 args(%arg0 = %q0) {
%q1 = qco.x %arg0 : !qco.qubit
qco.yield %false, %q1 : i1, !qco.qubit
}
default args(%arg0 = %q0) {
qco.yield %false, %arg0 : i1, !qco.qubit
}
Traits: AttrSizedResultSegments, RecursiveMemoryEffects, RecursivelySpeculatableImplTrait, SingleBlockImplicitTerminator<YieldOp>, SingleBlock
Interfaces: ConditionallySpeculatable, RegionBranchOpInterface
Attributes:¶
| Attribute | MLIR Type | Description |
|---|---|---|
cases | ::mlir::DenseI64ArrayAttr | i64 dense array attribute |
Operands:¶
Operand |
Description |
|---|---|
|
index |
|
variadic of QCO qubit value type or 1D tensor of QCO qubit value type values |
Results:¶
Result |
Description |
|---|---|
|
variadic of any non-token type |
|
variadic of QCO qubit value type or 1D tensor of QCO qubit value type values |
qco.inv (qco::InvOp)¶
Apply an inverse (i.e., adjoint) modifier to a collection of gates
Syntax:
operation ::= `qco.inv` custom<TargetAliasing>($region, $qubits_in)
attr-dict
( `:` `{` type($qubits_in)^ `}` `->` `{` type($qubits_out) `}` )?
A modifier operation that inverts the gates defined in its body region.
The operation takes a variadic number of qubits as inputs and produces corresponding output qubits.
The body region may contain unitary operations and memory-effect-free classical operations without regions.
Parameter computation is eager, as specified by the mqt.unitary contract; it need not be speculatable.
Classical SSA values may be captured from above, but every qubit used
in the body must be passed through a modifier operand and accessed via
its aliased block argument.
Classical memory access and non-unitary region operations are not allowed.
Unitary operations retain their required effects, including global phase.
Example:
%q_out = qco.inv (%q = %q_in) {
%q_1 = qco.s %q : !qco.qubit
qco.yield %q_1 : !qco.qubit
} : {!qco.qubit} -> {!qco.qubit}
Traits: RecursiveMemoryEffects, RecursivelySpeculatableImplTrait, SingleBlockImplicitTerminator<YieldOp>, SingleBlock
Interfaces: ConditionallySpeculatable, UnitaryOpInterface
Operands:¶
Operand |
Description |
|---|---|
|
variadic of QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
variadic of QCO qubit value type |
qco.iswap (qco::iSWAPOp)¶
Apply a iSWAP gate to two qubits
Syntax:
operation ::= `qco.iswap` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies a iSWAP gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.iswap %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.measure (qco::MeasureOp)¶
Measure a qubit in the computational basis
Syntax:
operation ::= `qco.measure` $qubit_in `:` type($qubit_in) attr-dict
Measures a qubit in the computational (Z) basis, collapsing the state and returning both the output qubit and a classical bit result.
Example:
%q_out, %result = qco.measure %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), OpAsmOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
1-bit signless integer |
qco.p (qco::POp)¶
Apply a P gate to a qubit
Syntax:
operation ::= `qco.p` `(` $theta `)` $qubit_in attr-dict `:` type($qubit_in)
Applies a P gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.p(%theta) %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetOneParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.pow (qco::PowOp)¶
Raise a unitary operation to a power
Syntax:
operation ::= `qco.pow` `(` $exponent `)` custom<TargetAliasing>($region, $qubits_in)
attr-dict
( `:` `{` type($qubits_in)^ `}` `->` `{` type($qubits_out) `}` )?
A modifier operation that raises the gates defined in its body region to a given power.
The operation takes a variadic number of qubits as inputs and produces corresponding output qubits.
The exponent can be an integer or a floating-point number.
r > 0: apply the gate raised to the r-th power.
r = 0: identity (no-op).
r < 0: equivalent to inv @ pow(-r) @ g.
The body region may contain unitary operations and memory-effect-free classical operations without regions.
Parameter computation is eager, as specified by the mqt.unitary contract; it need not be speculatable.
Classical SSA values may be captured from above, but every qubit used
in the body must be passed through a modifier operand and accessed via
its aliased block argument.
Classical memory access and non-unitary region operations are not allowed.
Unitary operations retain their required effects, including global phase.
Example:
%q_out = qco.pow(%exponent) (%q = %q_in) {
%q_1 = qco.s %q : !qco.qubit
qco.yield %q_1
} : {!qco.qubit} -> {!qco.qubit}
Traits: RecursiveMemoryEffects, RecursivelySpeculatableImplTrait, SingleBlockImplicitTerminator<::mlir::qco::YieldOp>, SingleBlock
Interfaces: ConditionallySpeculatable, UnitaryOpInterface
Operands:¶
Operand |
Description |
|---|---|
|
64-bit float |
|
variadic of QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
variadic of QCO qubit value type |
qco.r (qco::ROp)¶
Apply an R gate to a qubit
Syntax:
operation ::= `qco.r` `(` $theta `,` $phi `)` $qubit_in attr-dict `:` type($qubit_in)
Applies an R gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.r(%theta, %phi) %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetTwoParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
64-bit float |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.rccx (qco::RCCXOp)¶
Apply a relative-phase CCX gate to three qubits
Syntax:
operation ::= `qco.rccx` $qubit0_in `,` $qubit1_in `,` $qubit2_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in) `,` type($qubit2_in)
Applies a relative-phase CCX (RCCX) gate to three qubits and returns the
transformed qubits.
Example:
%q0_out, %q1_out, %q2_out = qco.rccx %q0_in, %q1_in, %q2_in : !qco.qubit, !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, ThreeTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
|
QCO qubit value type |
qco.reset (qco::ResetOp)¶
Reset a qubit to |0⟩ state
Syntax:
operation ::= `qco.reset` $qubit_in attr-dict `:` type($qubit_in)
Resets a qubit to the |0⟩ state, regardless of its current state, and returns the reset qubit.
Example:
%q_out = qco.reset %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, Idempotent, SameOperandsAndResultType
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.rx (qco::RXOp)¶
Apply an RX gate to a qubit
Syntax:
operation ::= `qco.rx` `(` $theta `)` $qubit_in attr-dict `:` type($qubit_in)
Applies an RX gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.rx(%theta) %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetOneParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.rxx (qco::RXXOp)¶
Apply an RXX gate to two qubits
Syntax:
operation ::= `qco.rxx` `(` $theta `)` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies an RXX gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.rxx(%theta) %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetOneParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.ry (qco::RYOp)¶
Apply an RY gate to a qubit
Syntax:
operation ::= `qco.ry` `(` $theta `)` $qubit_in attr-dict `:` type($qubit_in)
Applies an RY gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.ry(%theta) %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetOneParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.ryy (qco::RYYOp)¶
Apply an RYY gate to two qubits
Syntax:
operation ::= `qco.ryy` `(` $theta `)` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies an RYY gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.ryy(%theta) %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetOneParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.rz (qco::RZOp)¶
Apply an RZ gate to a qubit
Syntax:
operation ::= `qco.rz` `(` $theta `)` $qubit_in attr-dict `:` type($qubit_in)
Applies an RZ gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.rz(%theta) %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetOneParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.rzx (qco::RZXOp)¶
Apply an RZX gate to two qubits
Syntax:
operation ::= `qco.rzx` `(` $theta `)` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies an RZX gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.rzx(%theta) %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetOneParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.rzz (qco::RZZOp)¶
Apply an RZZ gate to two qubits
Syntax:
operation ::= `qco.rzz` `(` $theta `)` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies an RZZ gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.rzz(%theta) %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetOneParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.s (qco::SOp)¶
Apply an S gate to a qubit
Syntax:
operation ::= `qco.s` $qubit_in attr-dict `:` type($qubit_in)
Applies an S gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.s %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.sdg (qco::SdgOp)¶
Apply an Sdg gate to a qubit
Syntax:
operation ::= `qco.sdg` $qubit_in attr-dict `:` type($qubit_in)
Applies an Sdg gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.sdg %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.sink (qco::SinkOp)¶
Consume a qubit value (end of lifetime)
Syntax:
operation ::= `qco.sink` $qubit attr-dict `:` type($qubit)
Consumes a qubit SSA value and marks the end of its lifetime.
This operation is the canonical “sink” for QCO’s linear ownership: every qubit value must be consumed exactly once on all paths.
When converting back to QC (reference semantics), sinks corresponding to static qubits may be erased.
Example:
qco.sink %q : !qco.qubit
Interfaces: MemoryEffectOpInterface (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{MemoryEffects::Free on ::mlir::SideEffects::DefaultResource}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
qco.static (qco::StaticOp)¶
Retrieve a static qubit by index
Syntax:
operation ::= `qco.static` $index attr-dict `:` type($qubit)
The qco.static operation produces an SSA value representing a qubit
identified by a static index. This is useful for referring to fixed
qubits in a quantum program or to hardware-mapped qubits.
Example:
%q = qco.static 0 : !qco.qubit
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)
Effects: MemoryEffects::Effect{}
Attributes:¶
| Attribute | MLIR Type | Description |
|---|---|---|
index | ::mlir::IntegerAttr | 64-bit signless integer attribute whose value is non-negative |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.swap (qco::SWAPOp)¶
Apply a SWAP gate to two qubits
Syntax:
operation ::= `qco.swap` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies a SWAP gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.swap %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.sx (qco::SXOp)¶
Apply an SX gate to a qubit
Syntax:
operation ::= `qco.sx` $qubit_in attr-dict `:` type($qubit_in)
Applies an SX gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.sx %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.sxdg (qco::SXdgOp)¶
Apply an SXdg gate to a qubit
Syntax:
operation ::= `qco.sxdg` $qubit_in attr-dict `:` type($qubit_in)
Applies an SXdg gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.sxdg %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.t (qco::TOp)¶
Apply a T gate to a qubit
Syntax:
operation ::= `qco.t` $qubit_in attr-dict `:` type($qubit_in)
Applies a T gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.t %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.tdg (qco::TdgOp)¶
Apply a Tdg gate to a qubit
Syntax:
operation ::= `qco.tdg` $qubit_in attr-dict `:` type($qubit_in)
Applies a Tdg gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.tdg %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.u (qco::UOp)¶
Apply a U gate to a qubit
Syntax:
operation ::= `qco.u` `(` $theta `,` $phi `,` $lambda `)` $qubit_in attr-dict `:` type($qubit_in)
Applies a U gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.u(%theta, %phi, %lambda) %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetThreeParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
64-bit float |
|
64-bit float |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.u2 (qco::U2Op)¶
Apply a U2 gate to a qubit
Syntax:
operation ::= `qco.u2` `(` $phi `,` $lambda `)` $qubit_in attr-dict `:` type($qubit_in)
Applies a U2 gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.u2(%phi, %lambda) %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetTwoParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
64-bit float |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.unitary (qco::UnitaryOp)¶
Apply an explicitly represented unitary matrix
Syntax:
operation ::= `qco.unitary` $matrix $qubits_in attr-dict `:` type($qubits_in)
Applies a dense unitary matrix and returns the transformed qubits. The
matrix is stored in row-major order as a square rank-two tensor of
complex<f64> values. Its side length must be 2^n, where n is the
number of input qubits. Operand 0 labels the most-significant basis bit;
the last operand labels the least-significant bit. The deterministic
matrix verifier supports one to eight qubits and checks U^dagger U
entry-wise with absolute tolerance 1e-10.
Example:
%q_out = qco.unitary dense<[[(0.0,0.0), (1.0,0.0)],
[(1.0,0.0), (0.0,0.0)]]>
: tensor<2x2xcomplex<f64>> %q_in
: !qco.qubit
Traits: AlwaysSpeculatableImplTrait
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Attributes:¶
| Attribute | MLIR Type | Description |
|---|---|---|
matrix | ::mlir::ElementsAttr | constant vector/tensor attribute |
Operands:¶
Operand |
Description |
|---|---|
|
variadic of QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
variadic of QCO qubit value type |
qco.x (qco::XOp)¶
Apply an X gate to a qubit
Syntax:
operation ::= `qco.x` $qubit_in attr-dict `:` type($qubit_in)
Applies an X gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.x %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.xx_minus_yy (qco::XXMinusYYOp)¶
Apply an XX-YY gate to two qubits
Syntax:
operation ::= `qco.xx_minus_yy` `(` $theta `,` $beta `)` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies an XX-YY gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.xx_minus_yy(%theta, %beta) %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetTwoParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
|
64-bit float |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.xx_plus_yy (qco::XXPlusYYOp)¶
Apply an XX+YY gate to two qubits
Syntax:
operation ::= `qco.xx_plus_yy` `(` $theta `,` $beta `)` $qubit0_in `,` $qubit1_in attr-dict `:` type($qubit0_in) `,` type($qubit1_in)
Applies an XX+YY gate to two qubits and returns the transformed qubits.
Example:
%q0_out, %q1_out = qco.xx_plus_yy(%theta, %beta) %q0_in, %q1_in : !qco.qubit, !qco.qubit
Traits: AlwaysSpeculatableImplTrait, TwoTargetTwoParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
|
64-bit float |
|
64-bit float |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
|
QCO qubit value type |
qco.y (qco::YOp)¶
Apply a Y gate to a qubit
Syntax:
operation ::= `qco.y` $qubit_in attr-dict `:` type($qubit_in)
Applies a Y gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.y %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
qco.yield (qco::YieldOp)¶
Yield values from a QCO region
Syntax:
operation ::= `qco.yield` attr-dict ($targets^ `:` type($targets))?
Terminates a QCO region. Modifier regions yield their transformed qubits
in body-argument order: following each yielded qubit through unitary
input/output ties must reach the argument at the same position. Wire
permutations must be expressed with gates, not by reordering yields.
qco.if and qco.index_switch regions may additionally yield
ordinary classical SSA values before their transformed linear values.
The operands must match the expected output signature of the parent
operation.
Example:
%res_ctrl, %res_tgt:2 = qco.ctrl(%ctrl) targets(%a0 = %q0, %a1 = %q1) {
%a0_1, %a1_1 = qco.swap %a0, %a1 : !qco.qubit, !qco.qubit
qco.yield %a0_1, %a1_1 : !qco.qubit, !qco.qubit
} : ({!qco.qubit}, {!qco.qubit, !qco.qubit})
Traits: AlwaysSpeculatableImplTrait, HasParent<CtrlOp, IfOp, IndexSwitchOp, InvOp, PowOp>, ReturnLike, Terminator
Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), RegionBranchTerminatorOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
variadic of any non-token type |
qco.z (qco::ZOp)¶
Apply a Z gate to a qubit
Syntax:
operation ::= `qco.z` $qubit_in attr-dict `:` type($qubit_in)
Applies a Z gate to a qubit and returns the transformed qubit.
Example:
%q_out = qco.z %q_in : !qco.qubit
Traits: AlwaysSpeculatableImplTrait, OneTargetZeroParameter
Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), UnitaryOpInterface
Effects: MemoryEffects::Effect{}
Operands:¶
Operand |
Description |
|---|---|
|
QCO qubit value type |
Results:¶
Result |
Description |
|---|---|
|
QCO qubit value type |
Types¶
QubitType¶
QCO qubit value type
Syntax: !qco.qubit
The !qco.qubit type represents an SSA value holding a quantum bit
in the QCO dialect. Operations using this type consume input qubits
and produce new output qubits, making data dependencies explicit.
Example:
%q0 = qco.alloc : !qco.qubit
%q1 = qco.h %q0 : !qco.qubit
%q2 = qco.x %q1 : !qco.qubit
OpInterface definitions¶
UnitaryOpInterface (UnitaryOpInterface)¶
This interface provides a unified API for all operations that apply or produce a unitary transformation in the QCO dialect. This includes base gates, user-defined gates, modifier operations (control, inverse, power), and sequences.
The interface enables uniform introspection and composition capabilities across all unitary operations with value semantics.
Gate parameters must be finite, including values produced at runtime. Operation verification only checks direct constants as a sanity check.
Methods:¶
getNumQubits¶
size_t getNumQubits();
Returns the number of qubits acted on by the unitary operation.
NOTE: This method must be implemented by the user.
getNumTargets¶
size_t getNumTargets();
Returns the number of target qubits (excluding control qubits).
NOTE: This method must be implemented by the user.
getNumControls¶
size_t getNumControls();
Returns the number of control qubits (both positive and negative).
NOTE: This method must be implemented by the user.
getInputQubit¶
Value getInputQubit(size_t i);
Returns the i-th input qubit (targets + controls combined).
NOTE: This method must be implemented by the user.
getInputQubits¶
OperandRange getInputQubits();
Returns a range of all input qubits (targets + controls combined).
NOTE: This method must be implemented by the user.
getOutputQubit¶
Value getOutputQubit(size_t i);
Returns the i-th output qubit (targets + controls combined).
NOTE: This method must be implemented by the user.
getOutputQubits¶
ResultRange getOutputQubits();
Returns a range of all output qubits (targets + controls combined).
NOTE: This method must be implemented by the user.
getInputTarget¶
Value getInputTarget(size_t i);
Returns the i-th target input qubit.
NOTE: This method must be implemented by the user.
getInputTargets¶
OperandRange getInputTargets();
Returns a range of all target input qubits.
NOTE: This method must be implemented by the user.
getOutputTarget¶
Value getOutputTarget(size_t i);
Returns the i-th target output qubit.
NOTE: This method must be implemented by the user.
getOutputTargets¶
ResultRange getOutputTargets();
Returns a range of all target output qubits.
NOTE: This method must be implemented by the user.
getInputControl¶
Value getInputControl(size_t i);
Returns the i-th control input qubit.
NOTE: This method must be implemented by the user.
getInputControls¶
OperandRange getInputControls();
Returns a range of all control input qubits.
NOTE: This method must be implemented by the user.
getOutputControl¶
Value getOutputControl(size_t i);
Returns the i-th control output qubit.
NOTE: This method must be implemented by the user.
getOutputControls¶
ResultRange getOutputControls();
Returns a range of all control output qubits.
NOTE: This method must be implemented by the user.
getInputForOutput¶
Value getInputForOutput(Value output);
Returns the input qubit corresponding to the given output qubit.
NOTE: This method must be implemented by the user.
getOutputForInput¶
Value getOutputForInput(Value input);
Returns the output qubit corresponding to the given input qubit.
NOTE: This method must be implemented by the user.
getNumParams¶
size_t getNumParams();
Returns the number of parameters.
NOTE: This method must be implemented by the user.
getParameter¶
Value getParameter(size_t i);
Returns the i-th parameter.
NOTE: This method must be implemented by the user.
getParameters¶
OperandRange getParameters();
Returns a range of all parameters.
NOTE: This method must be implemented by the user.
isControlled¶
bool isControlled();
Returns true if the operation has any control qubits, otherwise false.
isSingleQubit¶
bool isSingleQubit();
Returns true if the operation only acts on a single qubit.
isTwoQubit¶
bool isTwoQubit();
Returns true if the operation acts on two qubits.
getBaseSymbol¶
StringRef getBaseSymbol();
Returns the base symbol/mnemonic of the operation.
NOTE: This method must be implemented by the user.
hasCompileTimeKnownUnitaryMatrix¶
bool hasCompileTimeKnownUnitaryMatrix();
Returns true if the operation has a compile-time known unitary matrix representation, false otherwise.
NOTE: This method must be implemented by the user.
getUnitaryMatrix1x1¶
bool getUnitaryMatrix1x1(Matrix1x1&out);
Populates the given 1x1 unitary matrix if possible.
getUnitaryMatrix2x2¶
bool getUnitaryMatrix2x2(Matrix2x2&out);
Populates the given 2x2 unitary matrix if possible.
getUnitaryMatrix4x4¶
bool getUnitaryMatrix4x4(Matrix4x4&out);
Populates the given 4x4 unitary matrix if possible.
getUnitaryMatrix8x8¶
bool getUnitaryMatrix8x8(Matrix8x8&out);
Populates the given 8x8 unitary matrix if possible.
getUnitaryMatrixDynamic¶
bool getUnitaryMatrixDynamic(DynamicMatrix&out);
Populates the given dynamic unitary matrix.
Passes¶
-combine-controlled-gates¶
Combine gates along the same ordered control and target wires
Combines qco.ctrl operations in the same block when every control and
target result is the corresponding input of the next operation. The
control/target partition must match. Linear qubit values make intervening
operations independent of these wires. The combined body is placed at the
second operation, retaining dominance of captured parameters. Bodies stay
within their block; the pass does not combine across loop iterations.
The pass concatenates bodies in order. Canonicalization or rotation fusion can simplify the combined body. Target pipelines preserve native control groups and their bodies, including fixed-parameter operations. They retain native controlled U fusion, then unroll surviving composites before routing and native synthesis. Combining is a separate pass so canonicalization does not recreate composite target operations.
-decompose-multi-controlled¶
Decompose controlled X/Y/Z/rotation/phase/SWAP gates and qco.rccx that act on at least min-qubits qubits
Decomposes multi-qubit controlled operations that act on at least
min-qubits qubits (default 3: everything wider than a two-qubit gate).
Supported shapes: qco.ctrl with a sole qco.x, qco.y, qco.z,
qco.rx, qco.ry, qco.rz, qco.swap, or qco.p body, and
qco.rccx. Rotation and phase angles may be constants or runtime SSA values,
including classical expressions inside the control region.
Controls over composite bodies are distributed across their operations
before decomposition, subject to the same width threshold. Nested inverse
bodies are unrolled in reverse order; power bodies require a constant integer
exponent and operations on disjoint wires.
Family |
Width (qubits) |
Decomposition |
|---|---|---|
X/Z |
3 |
Elementary CCX/CCZ; |
X/Z |
4 |
Elementary CCCX/CCCZ |
X/Z |
5 |
Specialized ancilla-free relative-phase |
X/Z |
6–33 |
da Silva-Park SP22 MCP(π) core ( |
X/Z |
≥34 |
Huang-Palsberg (HP24) borrowed-helper synthesis with partitioned incrementers |
Y |
≥3 |
X decomposition with |
RX/RY/RZ |
≥3 |
Balanced control halves with exact MCX and quarter-angle rotations; RX uses H-conjugated RZ |
Phase |
3 |
Optimized |
Phase |
4–5 |
Vale (Barenco-relative residual) |
Phase |
≥6 |
da Silva-Park SP22 linear-depth |
Constant phase |
any ( |
Routed through the Z path |
SWAP |
≥ |
|
Width is the total number of qubits the gate acts on (controls plus targets).
HP24 uses one dirty helper for an odd number of controls and two for an even number. These helpers are borrowed from the gate’s qubits and restored; no additional qubits are allocated.
Rotation synthesis uses a linear number of gates and no additional
qubits. Each half-MCX may borrow controls from the opposite half and
restores them coherently. The decomposition preserves phase, including
the conditional phase of a rotation by 2π.
Phase synthesis needs no additional qubits and uses a quadratic number of gates. Runtime angles require only power-of-two scaling.
For controlled SWAP, C ∪ {b} is the original control set together with
SWAP target b, and the MCX target is the other SWAP qubit a. The
emitted MCX is then lowered by the X path under the same min-qubits
threshold.
Intermediate building blocks may be left as qco.ctrl / qco.rccx when
min-qubits keeps them; the greedy rewriter lowers further when the
threshold allows.
Options¶
-min-qubits : Decompose controlled X/Y/Z/rotation/phase/SWAP gates and qco.rccx that act on at least this many qubits (must be at least 3; default 3 means wider than two-qubit).
-fuse-single-qubit-unitary-runs¶
Fuse single-qubit unitary runs using Euler resynthesis
Matches maximal runs of consecutive single-qubit unitary operations on the
same qubit wire (anchored at each run head), composes their constant unitary
matrices, and replaces a run with an equivalent sequence of basis gates when
beneficial: when the run contains a gate outside the target basis, or when
Euler resynthesis would shorten it (synthesizeUnitary1QEuler). Runs that are
already in the target basis and no shorter than the canonical synthesis
length are left unchanged.
The emitted basis is controlled via the basis option (e.g. zyz, zsxx).
A gphase correction is inserted when needed so the rewritten sequence
matches the composed matrix exactly (not only up to global phase).
The pass also composes supported named gates with dynamic f64 parameters
and emits conservative runtime sequences in the requested basis. Dynamic
pow and arbitrary dynamic unitaries remain unchanged.
Symbolic ZXZ/ZYZ chains reuse their angles directly in the u, zyz,
zxz, and zsxx bases; XZX/XYX chains do so in xzx, xyx, and r.
Either outer rotation may be absent. Gate operands are normalized modulo
\(4\pi\) before angle arithmetic, as in merge-single-qubit-rotation-gates.
H/RZ pairs in either order use direct angles in every basis, requiring at
most three gates (one in the u basis).
These conversions need no quaternion extraction or conditional expressions.
Other dynamic chains use runtime Euler-angle extraction.
Options¶
-basis : Target Euler basis (zyz, zxz, xzx, xyx, u, zsxx, r).
-hadamard-lifting¶
This pass attempts to move Hadamard gates as far away from measurements as possible by commuting them with Pauli gates. This is done in order to apply measurement lifting more efficiently, which is a sub-routine of qubit reuse. Additionally, it can change target and control qubits from Pauli-Z gates to make Hadamard lifting applicable. It also lifts Hadamard gates over CNOT gates if that moves a measurement directly after a control.
This pass lifts Hadamard gates away from the measurements in order to apply measurement lifting more effectively. Measurement lifting is a subroutine of the qubit reuse routine. The goal is to measure qubits earlier in the circuit to reuse them and to potentially remove some quantum gates.
Measurement lifting commutes measurements with Pauli and phase gates. However, the routine stops if a Hadamard gate is applied before the measurement takes place. Hadamard lifting attempts to move Hadamards further in front of the circuit, in order to improve the results provided by measurement lifting.
Hadamard lifting uses the following commutation rules:
┌───┐ ┌───┐ ┌───┐ ┌───┐
─┤ X ├─┤ H ├─ => ─┤ H ├─┤ Z ├─
└───┘ └───┘ └───┘ └───┘
┌───┐ ┌───┐ ┌───┐ ┌───┐
─┤ Z ├─┤ H ├─ => ─┤ H ├─┤ X ├─
└───┘ └───┘ └───┘ └───┘
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───────┐
─┤ Y ├─┤ H ├─ => ─┤ H ├─┤ Y ├─, while adding a global phase: │ G(pi) │
└───┘ └───┘ └───┘ └───┘ └───────┘
Hadamard lifting is only applied to commute Hadamard gates further in front in the circuit, not the other way around. A global phase is added if the Hadamard gate is commuted with a Pauli-Y gate, as YH = -HY.
The routine is only applied to non-controlled Pauli and Hadamard gates.
In order to reduce multi-qubit gates, a second transformation routine is applied. Using the commutation rule
┌───┐┌───┐┌───┐
──■── ┤ H ├┤ X ├┤ H ├
┌─┴─┐ => │───│└─┬─┘│───│
┤ X ├ ┤ H ├──■──┤ H ├
└───┘ └───┘ └───┘
the following transformation is applied to circuits where a Hadamard gate follows a target gate of a controlled Pauli-X gate, which is followed by a measurement:
┌───┐┌───┐┌───┐
──■───────────── ┤ H ├┤ X ├┤ H ├──
┌─┴─┐┌───┐┌──────┐ => │───│└─┬─┘│───┘──┐
┤ X ├┤ H ├┤ Meas │ ┤ H ├──■──┤ Meas │
└───┘└───┘└──────┘ └───┘ └──────┘
Afterward the measurement lifting routine could transform the CNOT into a classically controlled Pauli-X.
-legalize-control-flow¶
Legalize control flow for the selected payload
Reads the selected payload from mqt.target_env. The pass retains covered
QCO and SCF branches and loops, lowers unsupported index switches to nested
forward branches when possible, and rejects residual structural control
flow that the payload does not support.
Switch expansion checks the selected forward-branching depth limit before
rewriting and limits the resulting total control-flow nesting depth to 256.
This compiler safety limit does not restrict retained native switches.
Run this pass after unroll-loops-for-payload, constant propagation, and
QCO cleanup so newly constant control flow can fold before legality checks.
Before conversion, the pass checks that all SCF loops carry linear QCO
state through iteration arguments instead of capturing it.
-measurement-lifting¶
Move measurements before compatible unitary operations
This pass moves computational-basis measurements toward the start of a
circuit. A measurement of a control can move before the controlled
operation. A single-qubit diagonal operation immediately before a
measurement is removed because it cannot affect either the outcome or the
post-measurement qubit. Multi-qubit diagonal operations are only commuted:
removing them could discard phase kickback on another qubit. Measurements
also move before x and y, with the classical result inverted.
Earlier measurements enable qubit reuse and subsequent optimizations.
-merge-single-qubit-rotation-gates¶
Merge consecutive named single-qubit gates into U gates
Merges maximal runs of at least two supported gates on the same qubit into at most one U gate. Isolated gates remain unchanged.
Supported gate types: rx, ry, rz, p, r, u2, u, x, y,
z, h, s, sdg, t, tdg, sx, sxdg, and id.
Constant runs use the matrix composition and Euler synthesis shared with
native-target synthesis. Identity runs need no gate.
Symbolic RZ-RX-RZ and RZ-RY-RZ chains reuse their Euler angles directly;
either outer RZ may be absent. H/RZ pairs in either order also use direct
angles via Hadamard conjugation. Other dynamic chains use quaternion
composition and emit arith / math expressions for runtime Euler angles.
Before symbolic angle arithmetic, each evaluated gate operand is normalized modulo \(4\pi\), preserving its full unitary under control. Constants fold immediately; symbolic operands use \(4\arctan(\tan(\theta/4))\), which remains bindable after Qiskit and jeff export. This bounds each gate operand to \([-2\pi, 2\pi]\) without restricting shared scalar inputs or expressions.
A gphase correction preserves exact matrix equality, including inside
controlled bodies. The pass combines, normalizes, and removes trivial
corrections in their respective scopes. Euler angles are not unique;
their particular representative is not part of the pass contract.
-pauli-twirl-2q-gates¶
Apply Pauli twirling to supported two-qubit gates
Surrounds each supported two-qubit gate with two single-qubit Pauli gates before and after the gate. The pass selects uniformly from the 16 twirls that preserve the exact gate unitary and inserts a global-phase correction when required. Identity gates remain explicit.
The pass supports controlled X and Z gates with one control and one target, ECR gates, and iSWAP gates. Gates nested in modifier regions remain unchanged.
Options¶
-seed : Seed used to select Pauli twirls.
-place-and-route¶
Place and route a program for an explicit target topology
This pass maps top-level scalar and tensor-backed dynamically allocated
qubits in a quantum program to the static sites of a CompilerTarget. It
requires an explicit undirected coupling topology: arbitrary one-qubit
operations pass through, and every two-qubit operation is routed to
adjacent target vertices. Higher-arity unitaries must be decomposed before
mapping. Target-defined site identifiers are retained in the resulting
qco.static operations.
The entry function and supported structured-control-flow regions must each contain one block. Calls that carry qubits must be inlined before mapping. Adaptive classical dependencies must be represented by SSA def-use chains. Classical side effects must use CBit operations; opaque calls and other memory effects must be lowered before mapping. Tensor allocations must follow a flat extract/insert/deallocate chain without tensor control flow.
First, the pass assigns static qubits to the dynamically allocated ones by creating an initial dynamic-to-static
mapping, which is referred to as the initial layout. The layout covers the
complete target. The pass materializes all target sites as routing workspace
and carries them through structured control flow. Cleanup after mapping
removes unused sites. The pass traverses the circuit
and inserts qco.swap operations to ensure that all two-qubit operations
are executable on the target architecture, a process known as routing.
Each routing block has one wire slot per physical site. Its layout is the
sole mapping from logical qubits to those sites. Candidate evaluation keeps
empty cursors for idle sites; final placement materializes their qubits.
Virtual SWAPs exchange the cursors at their two sites, matching the SSA
successors of emitted SWAPs. Both traversal directions stop at region
boundaries, including on topology-only targets. Child blocks start from
the entry layout, branches converge, and loop bodies restore their entry
layout. For scf.while, the before-region exit places the after region
and loop results; the after region restores the loop-entry layout.
For routing, the pass first divides the circuit into layers. A layer is a set of independently executable (sequences
or blocks of) two-qubit operations. Subsequently, the pass performs an A* search for each layer to find and insert a
sequence of SWAP operations that makes these two-qubit operations executable. The A* search also considers
subsequent layers, which are determined by the nlookahead parameter.
The cost function of the A* search is defined as follows:
f(n) = g(n) + h(n)g(n) = alpha * depth(n)h(n) = sum(pow(lambda, i) * h(gate, p) for [i, gate] in enumerate(window))
Where:
pis the dynamic-to-static mapping associated with search noden.windowcontains at most1 + nlookaheadtwo-qubit operations in program order.depth(n)returns the distance from the nodento the root node.dist(i, j)returns the distance between the qubitsiandjon the target’s coupling graph.h(gate, p)isdist(p[gate.first], p[gate.second]) - 1.
When a native-cost estimate is available and standalone SWAP cost is uniform
across target edges, refinement, scoring, and final routing measure both terms in native
two-qubit gates. The first SWAP receives the signed adjustment
cost(prefix + SWAP) - cost(prefix) - cost(SWAP) for the pending two-qubit
run. Fusion can reduce this cost. Extending a cheaper prefix can also raise
it. This adjustment is applied once. Later steps use standalone SWAP cost.
Layout dominance compares accumulated cost, including the adjustment.
Backward refinement prepends the SWAP to the already-routed suffix instead.
Both directions compose matrices in circuit order. Each traversal starts
with empty accounting state and reuses its numerical caches. Missing or
nonuniform SWAP costs retain distance costs for all searches.
This is heuristic search, not a guarantee of optimal native cost.
Routing uses undirected target connectivity. Target-native synthesis realizes operations and inserted SWAPs in a supported operand direction.
Each search limits retained nodes using search-memory-limit, an estimated
byte budget for the nodes and their qubit layouts (256 MiB by default).
At least the root node is retained; zero disables node expansion.
The pass stops allocating nodes at the limit, checks the remaining queue,
and, if no solution is queued, completes the route with SWAPs that reduce
the leading pair’s distance. This always makes progress on a connected
target. Changing the budget can change the selected initial layout and
final gate count or depth; more memory does not guarantee fewer gates.
Queue, visited-map, allocator, and linear fallback workspace overhead are
additional. Each concurrent trial reuses this bounded storage across its
searches and releases it when the trial finishes. Final SWAP insertion uses
separate storage after the trials finish. Target distance caches and
O(number of sites) wire, layout, and optional native-cost state for each
live region are separate; this is not a process memory limit. Candidate scoring
does not clone IR or execute native-synthesis passes. A shared immutable
table holds at most 1024 precomputed numerical native counts (about 264 KiB
of payload plus indexing). Each live region’s tracker caches up to 64
additional counts (about 17 KiB). Exact matrix, entangler, and compilation
seed matches preserve numerical results; misses use normal analysis.
To iteratively refine the mapping, the pass performs multiple forward and backward traversals of the circuit. In
each traversal, the pass routes the circuit and updates the dynamic-to-static mapping based on the routing decisions
made during that traversal. By performing multiple traversals, the pass can iteratively refine the mapping and
potentially find a more optimal solution. This behavior is controlled by the niterations parameter.
Programs with supported control flow keep the identity layout immediately when all two-qubit interactions are already adjacent on the target. This avoids constructing and refining alternative layouts.
Otherwise, the pass option ntrials determines the number of initial refinement trials:
a greedy layout when available, then identity if a slot remains, and random
layouts for the remaining slots. Every trial uses niterations refinement
rounds before scoring; zero scores the initial layouts directly.
It defaults to the number of available logical CPUs reported by LLVM, taking
CPU affinity into account, with a minimum of one. Disabling multithreading
runs the same trials sequentially; it does not reduce the trial count.
If compiled with multi-threading on, these trials will be executed in parallel.
Set both ntrials and seed explicitly for reproducible results across
machines with different CPU counts.
For programs with two-qubit interactions, the pass also constructs an
interaction-weighted greedy layout. Statically counted scalar-qubit
scf.for loops contribute their body interactions weighted by the trip
count, including nested loops. Zero or unknown trip counts and other control
flow keep the identity/random fallback. Layout costs use saturating weights;
routing restores the layout at loop boundaries.
For disjoint interaction paths and isolated qubits, it first tries to place
them along one path through the target sites. The walk prefers neighbors
with fewer unused edges and accepts only a complete placement; it does not
backtrack. A complete path placement needs no routing and skips layout
trials, as does an executable identity layout. Other interaction graphs and
failed walks use the weighted greedy layout, which places frequently
interacting qubits nearby, starting at a central target site. This layout
occupies the first trial slot.
With an explicit native operation set and a usable entangler basis, each candidate is scored by a read-only forward traversal of original gates and virtual SWAPs. The estimate shares native support, operand direction, and numerical decomposition with native synthesis. It composes constant runs on each physical pair, including intervening constant single-qubit gates, and preserves supported operations unless fusion reduces two-qubit count. Immediate inverse pairs do not break an earlier pending run. Other pair changes, symbolic gates, barriers, measurements, resets, and region boundaries end runs. Branch reconciliation and loop-restoration SWAPs are included.
The pass ranks candidates by estimated native two-qubit count, then estimated qubit-dependency depth, retaining the earlier candidate on an exact tie. Counts are static: loop bodies and branch arms contribute their operations without execution frequencies. Depth is the maximum within any block; it excludes classical scheduling and is not a runtime control-flow bound. General canonicalization and cleanup are not simulated, so the estimate can differ from final synthesis. Only the winning route is materialized.
If native scoring is unavailable, the candidate uses its forward traversal’s SWAP count as a fallback. Candidates with an available native estimate rank before fallback candidates. Topology-only mapping remains supported; final target compilation diagnoses unsupported native operations. Scoring and final routing use the same traversal hooks and first-SWAP cost guidance.
Options¶
-nlookahead : The number of lookahead steps.
-alpha : The alpha cost factor. Must be finite and > 0.
-lambda : The lambda factor in the cost function.
-niterations : The number of forwards and backwards traversal to improve the initial layout. Must be >= 0.
-ntrials : The total number of greedy/identity/random trials of the forwards and backwards mechanism. Defaults to the available logical CPU count. Must be > 0.
-seed : A seed used for randomization.
-search-memory-limit : Estimated node and layout bytes per search. Zero disables node expansion.
Statistics¶
num-inserted-swaps : The number of inserted SWAPs
-quantum-loop-unroll¶
Unroll bounded loops containing quantum operations
This pass unrolls scf.for operations which contain quantum operations. Classical loops are not unrolled.
Because the pass uses the mlir::loopUnrollByFactor utility internally, the same assumptions and restrictions hold for both.
See the MLIR documentation for more information.
Options¶
-unroll-factor : Use this unroll factor for all loops being unrolled, set it to -1, and it will fully unroll.
-remove-dead-gates¶
Remove quantum gates whose results cannot be observed
This pass removes dead quantum operations by walking backwards from
qco.sink and qco.reset operations. It removes memory-effect-free
unitary operations and other removable quantum operations until it reaches
an operation whose result is observable or whose effects must be preserved.
Dead-gate removal is explicit rather than part of canonicalization because
measurement-free programs may intentionally represent state preparation or
larger quantum building blocks. The mqt-qubit-reuse pipeline runs this
pass before attempting to reuse qubits.
-replace-classical-controls¶
Replace measured controls and diagonal targets with classical data flow
This pass rewrites a qco.ctrl that immediately consumes one or more
measured controls into one qco.if. Multiple measurement outcomes are
combined into the condition, while unmeasured controls remain quantum
controls inside it. The controlled body is not duplicated.
The pass also simplifies supported diagonal operations with measured
targets. A measured rz target becomes an outcome-dependent phase on the
controls. One measured rzz target turns the operation into an
outcome-dependent rz on the other target; two measured targets leave only
phase kickback on coherent controls. If every participating qubit has been
measured, the diagonal operation is removed. Other single-target phase
gates can exchange a measured target with an unmeasured control before the
general rewrite.
Replacing quantum controls with classical conditions reduces quantum interactions and can enable qubit reuse and subsequent optimizations.
-reuse-qubits¶
This pass attempts to utilize qubit reuse to reduce the number of resources required for a quantum program. It does so by looking for qubits that do not interact with each other directly or indirectly and attempting to reset and reuse one of them for the other. By combining this pass with the measurement-lifting and replace-classical-controls passes, it is possible to reduce the number of qubits required for a quantum program significantly.
This pass searches for qubits that do not interact with each other directly or indirectly and attempts to reset
and reuse one of them for the other. Improved results can be achieved by combining this pass with the
measurement-lifting and replace-classical-controls passes, which can reduce the number of qubits required for a quantum program significantly.
Only single-qubit allocations are considered for reuse, no qtensor allocations.
Before rewriting non-unitary functions, the pass summarizes the memory effects of unitary helpers and their transitive callees. Proven effect-free calls permit reuse; scoped global phases and unknown effects remain barriers. Unitary helpers are not rewritten, and summaries are discarded after each pass invocation.
Use the mqt-qubit-reuse pipeline to run the preparation passes followed by this pass.
-target-native-synthesis¶
Synthesize operations for the native target basis
Reads the typed mqt.target_env module attribute and lowers non-native
unitary operations to one complete synthesis basis supported throughout the
compiler target. Constant two-qubit runs, including interleaved single-qubit
gates, are resynthesized together when this strictly reduces the number of
native two-qubit gates compared with preserving supported operations and
lowering the others individually. Native support includes ordered site
placements. Equal-cost runs and runs whose numerical decomposition fails
are left for individual lowering. Barriers, non-unitary operations, and
unavailable matrices stop a run.
For non-U bases, constant single-qubit runs are fused when they contain an unsupported gate or resynthesis reduces their gate count. Native gates outside the selected synthesis basis remain supported. Optional symbolic fusion preserves native runs and uses only direct Euler identities; other runs are left for individual lowering to retain exportability.
Unknown native-operation metadata is valid when no surviving unitary operation needs it. Otherwise, the pass fails when the metadata is unknown, no complete basis exists, or an operation has no compile-time unitary matrix and cannot be synthesized as a parameterized single-qubit gate or a single-controlled phase gate.
Numerical decomposition failure is diagnosed before emitting a replacement. The input may be modified on failure.
Options¶
-seed : Seed for numerical synthesis retries.
-unroll-loops-for-payload¶
Unroll static loops required by the payload or qubit placement
Reads the selected payload from mqt.target_env and fully unrolls static
scf.for operations that its counted-iteration capability does not cover.
Tensor-carrying loops remain indexed for Adaptive QIR on all-to-all targets
whose native operations have no site tuples. Other targets and payloads
require fixed quantum indices and specialize these loops. Scalar loops
remain when supported by the payload. Run QCO cleanup first to move
complete constant-slot tensor accesses outside structured control flow.
Constant inner sweeps can become scalar-qubit loops after tensor cleanup,
preserving enclosing loops. This analysis stays within nested loop chains.
The max-operations option limits cloned body operations to one billion by default.
Run constant propagation and QCO cleanup again after this pass, then
legalize-control-flow to check the remaining branches and loops.
After folding static branches, the pass checks that all SCF loops carry
linear QCO state through iteration arguments instead of capturing it.
Options¶
-max-operations : Maximum number of cloned loop-body operations.
-verify-target-conformance¶
Verify that a mapped program conforms to its compiler target
Reads the typed mqt.target_env module attribute and verifies that every
qubit is assigned to a target site and every remaining quantum operation is
native for the compiler target. Unknown native-operation metadata is valid
when the surviving program contains no unitary, measurement, or reset
operation that needs the metadata.