Skip to content

gate-decomposition keeps the original gate, and the RX sequence has a sign error #3

Description

@cleitonaugusto

I was reading the O2/O3 pipeline and found two separate problems in
gate-decomposition. They are independent of each other, but both are in the same
file, so I am putting them in one issue.

First one: the original gate is never removed.

#dialect quantum
module @m {
    func @f(%q0: qubit) -> qubit {
        %a = "quantum.t"(%q0) : (qubit) -> qubit
        return %a
    }
}

Running GateDecomposition::new(Provider::IbmEagle) on that:

before: ["t"]
after:  ["t", "rz(0.785398)"]

The new rz is not dangling, it is wired into the chain:

block: ["quantum.rz", "quantum.t", "core.return"]
quantum.rz  inputs=[1v1]  results=[3v1]
quantum.t   inputs=[3v1]  results=[2v1]

So the circuit becomes Rz(pi/4) and then T. Checking the matrices, T . Rz(pi/4)
is S up to global phase, so a T gate turns into an S gate. This is not specific
to T. gate_decompose.rs has no ops.remove and no retain anywhere in it, so
nothing is ever deleted and every decomposition doubles.

test_ibm_decomposes_h does not catch it because it asserts that rz, sx and cx
are present, and never that quantum.h is gone.

Second one: the RX sequence has a sign error, and RX(0) compiles to Z.

Around line 231 the emitted sequence is, in circuit order:

rz(-pi/2), sx, rz(pi + theta), sx, rz(pi/2)

I compared that against RX(theta) up to global phase for theta in
{0, 0.3, 1.0, pi/2, pi, 2.2, -0.7, 3.9}. It does not match at any of them.
Changing the first rotation to rz(+pi/2) matches at all of them.

The clearest case is theta = 0, which should be the identity:

RX(0) as emitted, normalised by global phase:

    [[1,  0],
     [0, -1]]

which is Z, not I.

The comment above that arm gives two different formulas, one with six gates and
one with five. That may be where the sign was lost.

One thing that changes how urgent this is, so I would rather say it than leave it
out: through the CLI this pass currently does nothing. GateDecomposition::default()
resolves the provider from a lift_provider metadata key, and as far as I can tell
nothing in the repo ever writes that key, so it falls back to Provider::Simulator
and the pass returns Unchanged. Both problems above show up when the pass is built
with an explicit provider, which is what GateDecomposition::new is for.

Built from 9ca5a92, tagged v0.4.4.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions