Skip to content

[STIM] Support Generalized Pauli Product Gates - #3556

Open
João Boechat (joao-boechat) wants to merge 14 commits into
mainfrom
joaoboechat/support-generalized-pauli-product-gates
Open

[STIM] Support Generalized Pauli Product Gates#3556
João Boechat (joao-boechat) wants to merge 14 commits into
mainfrom
joaoboechat/support-generalized-pauli-product-gates

Conversation

@joao-boechat

@joao-boechat João Boechat (joao-boechat) commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

This PR adds QIR compiler support for Stim's generalized Pauli-product gates: MPP, SPP, and SPP_DAG. For more info on how they work, I recommend the official stim docs.

High-level changes

  • Lexer: Starts treating Pauli targets (e.g. X1, Y1, Z1) and loss targets (e.g. L1) as separate tokens, rather than the general "instruction_name".
  • Parser: Groups *-connected Pauli targets into a single PauliProduct AST node. It combines factor negations with XOR and represents each term as a PauliFactor.
  • Compiler: Adds shared validation, canonicalization, and emission for generalized Pauli products.

Canonicalization

Before emission, the compiler canonicalizes each product by sorting factors by qubit, multiplying repeated same-qubit Paulis, and tracking the resulting phase i^k. Odd phases are rejected because they produce an anti-Hermitian product. A -1 phase negates the canonical product, while factors reducing to identity are removed.

Decomposition

To emit the product, the compiler:

  1. Rotates each Pauli factor into the Z basis.
  2. Accumulates the product parity onto one focus qubit using CNOTs.
  3. Performs the central operation:
    • MPP: measure the focus qubit.
    • SPP: apply S.
    • SPP_DAG: apply S_DAG.
  4. Reverses the CNOTs and basis rotations.

Inversion logic

For MPP, negation flips the measurement result. For phase gates, negation reverses the phase direction:

let adjoint = (instruction.name == "SPP_DAG") ^ negated;

Identity products

An MPP target whose factors all cancel measures the identity, which is deterministic but still has to append a result to the measurement record. We don't have an instruction that appends to the measurement record without acting on a qubit, so these targets are rejected for now. Once that instruction exists, MPP will be able to support them.

SPP and SPP_DAG don't have this limitation: phasing the identity is a global phase, so those targets are emitted as no-ops.

@joao-boechat João Boechat (joao-boechat) changed the title Joaoboechat/support generalized pauli product gates [STIM] Support Generalized Pauli Product Gates Aug 7, 2026
Comment thread source/compiler/stim_compiler/src/qir.rs Dismissed

@fedimser Dima Fedoriaka (fedimser) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I would suggest adding a comment for each function explaining what it's doing (when it's not obvious from the name), which would make it easier to review. For example, decompose_pauli_product doesn't literally decompose Pauli product, but decomposes an instruction whose target is Pauli product.

Comment thread source/compiler/stim_compiler/src/parser.rs Outdated
Comment thread source/compiler/stim_compiler/src/parser.rs Outdated
Comment thread source/compiler/stim_compiler/src/qir.rs
Comment thread source/compiler/stim_compiler/src/lex.rs
Comment thread source/compiler/stim_compiler/src/parser.rs Outdated
Comment thread source/compiler/stim_compiler/src/parser.rs
}

#[test]
fn pauli_product_negated_second_factor() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something about this seems off to me. The negation is on the Y1 target, and if this was a naked Pauli the negative sign would be inside the Pauli string, not in front of it: Pauli(-Y 1).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the PauliProduct be displayed in a way that reflects this hierarchy?
i.e. PauliProduct(Pauli(X 0)*Pauli(-Y 1))

@ScottCarda-MS Scott Carda (ScottCarda-MS) Aug 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Processing the negations during the parsing (i.e. cancelling out even negations) seems like we are doing evaluation at the wrong step of the process.


#[test]
fn mpp_product_folding_to_identity_yields_error() {
// this is temporary

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment left over from something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is a temporary behavior that will disappear in a PR that's coming soon. I just left the comment there to remind myself of removing this test case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants