Motivation
In Quantum Error Correction (QEC), any arbitrary error can be represented as a combination of Pauli components: bit-flip ($X$), phase-flip ($Z$), and their combination ($Y$). Using this decomposition, a broad class of physical errors can be reduced to a small set of correctable primitives.
Another important idea at the core of QEC is parity measurement. In classical error correction, one can measure a bit and check its value before deciding whether to correct it. However, we cannot take this for granted in quantum computing: measuring a qubit directly collapses its state and invalidates further quantum computation.
Parity measurements address this problem. When used carefully, they allow us to determine which qubit was affected by an error and correct it accordingly.
As QEC constructions become more sophisticated, circuits that detect errors (syndromes) and conditionally recover from them remain structurally important and are repeated many times. Predicated Pauli operations appear naturally in these workflows, for example:
Here, r is the result of a parity measurement.
Problem Statement
Repeating the construction above throughout the code works correctly, but it hides a recurring semantic pattern from the compiler.
As a result, the generated code contains excessive branching. This hurts readability and makes it more difficult for the compiler to identify optimizations such as:
- Reordering
- Simplification
- Lowering to target-specific implementations
Proposed Solution
Ideally, we would implement three operations with the following signatures, generalizing the idea above to allow the measurement result to be an array:
operation PX(results: Result[], qubit: Qubit) : Unit
operation PY(results: Result[], qubit: Qubit) : Unit
operation PZ(results: Result[], qubit: Qubit) : Unit
Their semantics would be straightforward:
- Apply the corresponding Pauli operation when the parity encoded by the result array is odd.
- Apply no operation when the parity is even.
These operations would call the following runtime intrinsics:
__quantum__qis__px__body(results: Result[], length: Int, qubit: Qubit)
__quantum__qis__py__body(results: Result[], length: Int, qubit: Qubit)
__quantum__qis__pz__body(results: Result[], length: Int, qubit: Qubit)
From there, we have two implementation alternatives:
- Emit them as simulatable intrinsics, with their simulator implementations written in Q#.
- Emit them as intrinsics, with their simulator implementations added to
compiler/qsc_eval/src/intrinsic.rs.
Because the signatures include array parameters, these operations would work only under the Adaptive RI profile. We should make this restriction clear through operation configuration and ensure that appropriate diagnostics are emitted when a user targets a different profile.
Open Questions
- Should these operations support the
Adjoint functor?
- How should these operations appear in circuit diagrams?
- Should we support only predicated Pauli operations (
PX, PY, and PZ), or other predicated operations such as PH as well?
- Should we expose a single
PredicatedPauli operation and pass the corresponding Pauli as an argument?
- How can we ensure that the engine team supports these new intrinsics?
Motivation
In Quantum Error Correction (QEC), any arbitrary error can be represented as a combination of Pauli components: bit-flip ($X$ ), phase-flip ($Z$ ), and their combination ($Y$ ). Using this decomposition, a broad class of physical errors can be reduced to a small set of correctable primitives.
Another important idea at the core of QEC is parity measurement. In classical error correction, one can measure a bit and check its value before deciding whether to correct it. However, we cannot take this for granted in quantum computing: measuring a qubit directly collapses its state and invalidates further quantum computation.
Parity measurements address this problem. When used carefully, they allow us to determine which qubit was affected by an error and correct it accordingly.
As QEC constructions become more sophisticated, circuits that detect errors (syndromes) and conditionally recover from them remain structurally important and are repeated many times. Predicated Pauli operations appear naturally in these workflows, for example:
Here,
ris the result of a parity measurement.Problem Statement
Repeating the construction above throughout the code works correctly, but it hides a recurring semantic pattern from the compiler.
As a result, the generated code contains excessive branching. This hurts readability and makes it more difficult for the compiler to identify optimizations such as:
Proposed Solution
Ideally, we would implement three operations with the following signatures, generalizing the idea above to allow the measurement result to be an array:
Their semantics would be straightforward:
These operations would call the following runtime intrinsics:
From there, we have two implementation alternatives:
compiler/qsc_eval/src/intrinsic.rs.Because the signatures include array parameters, these operations would work only under the Adaptive RI profile. We should make this restriction clear through operation configuration and ensure that appropriate diagnostics are emitted when a user targets a different profile.
Open Questions
Adjointfunctor?PX,PY, andPZ), or other predicated operations such asPHas well?PredicatedPaulioperation and pass the corresponding Pauli as an argument?