Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Controlled gates (other than RX,RY, RZ) with symbolic parameters don't compile #887

Open
steve-jeffrey opened this issue May 12, 2023 · 4 comments

Comments

@steve-jeffrey
Copy link

Description

Controlled gates (other than RX, RY, RZ) with symbolic parameters will not compile.

Controlled RX, RY, RZ gates with symbolic parameters will compile (see related issue).

How to Reproduce

Either run the example code provided, or:

echo 'DECLARE phi REAL[1];CONTROLLED PHASE(phi) 1 0' | docker run --rm -i rigetti/quilc

and

echo 'DECLARE phi REAL[1];CONTROLLED XY(phi) 2 0 1' | docker run --rm -i rigetti/quilc

Code Snippet

from pyquil import Program, get_qc
from pyquil.gates import RY, XY, PHASE

program = Program()
phi = program.declare("phi", "REAL")
program += RY(phi, 0).controlled(1)  # Works
program += XY(1.23, 0, 1).controlled(2)  # Works
program += PHASE(phi, 0).controlled(1)  # Fails: Condition CL-QUIL::COMPILER-DOES-NOT-APPLY was signalled.
program += XY(phi, 0, 1).controlled(2)  # Fails: Condition CL-QUIL::UNKNOWN-GATE-PARAMETER was signalled.

qc = get_qc("3q-qvm")
native = qc.compiler.quil_to_native_quil(program)

Error Output

Condition CL-QUIL::COMPILER-DOES-NOT-APPLY was signalled

or

Condition CL-QUIL::UNKNOWN-GATE-PARAMETER was signalled

Environment

Quilc version 1.26.0

@ecpeterson
Copy link
Contributor

ecpeterson commented May 12, 2023

Fair enough. A generic analytic compilation routine for parametric gates, including controlled versions of simple gates, is somewhere between hard and impossible — but for specific cases, we can probably figure it out.

Definitely CONTROLLED PHASE is a variant of CONTROLLED RZ up to local phase. I think, but I am not certain, that CONTROLLED XY p q r is XY q r followed by a (CZ p q)-conjugate of XY q r, maybe with some more local gates to balance out phases. One would have to chase it out.

@ecpeterson
Copy link
Contributor

It doesn't look like there's any phase to chase. Here's some quilc -P -m output:

XY(0.4) 0 1
CZ 2 1
XY(-0.4) 0 1
CZ 2 1

#Matrix read off from input code (unitary)
#<MATRIX/COMPLEX-DOUBLE-FLOAT (8x8):
#   1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.921 + 0.000j     0.000 + 0.389j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.389j     0.921 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j>

which looks like the controlled version of

XY(0.8) 0 1

#Matrix read off from input code (unitary)
#<MATRIX/COMPLEX-DOUBLE-FLOAT (4x4):
#   1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.921 + 0.000j     0.000 + 0.389j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.389j     0.921 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j>

For funsies, here's a recipe for CONTROLLED CONTROLLED XY(0.8) 3 2 1 0:

CONTROLLED CONTROLLED Z 3 2 1
XY(-0.4) 0 1
CONTROLLED CONTROLLED Z 3 2 1
XY(0.4) 0 1

#Matrix read off from input code (unitary)
#<MATRIX/COMPLEX-DOUBLE-FLOAT (16x16):
#   1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.921 + 0.000j     0.000 + 0.389j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.389j     0.921 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j>

and a recipe for FORKED XY(0.35, 0.25) 2 1 0:

XY(0.3) 0 1
CZ 2 1
XY(0.05) 0 1
CZ 2 1

#<MATRIX/COMPLEX-DOUBLE-FLOAT (8x8):
#   1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.985 + 0.000j     0.000 + 0.174j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.174j     0.985 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.992 + 0.000j     0.000 + 0.125j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.125j     0.992 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     1.000 + 0.000j>

@stylewarning: This sounds like it should be tagged "good first issue". I don't have tag permissions, myself.

@ecpeterson
Copy link
Contributor

Just to finish the job, here's an example circuit which relates CONTROLLED PHASE and CONTROLLED RZ by implementing the identity:

CONTROLLED RZ(0.4) 1 0
RZ(0.2) 1

CONTROLLED PHASE(-0.4) 1 0


#Matrix read off from input code (unitary)
#<MATRIX/COMPLEX-DOUBLE-FLOAT (4x4):
#   0.995 - 0.100j     0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.995 - 0.100j     0.000 + 0.000j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.995 - 0.100j     0.000 + 0.000j
#   0.000 + 0.000j     0.000 + 0.000j     0.000 + 0.000j     0.995 - 0.100j>

@ecpeterson
Copy link
Contributor

ecpeterson commented Mar 7, 2024

The decomposition of FORKED ... FORKED XY proposed here is what's already done in ucr-explode.lisp . To extend that to handle CZ decompositions of FORKED XY, we need only change uniformly-controlled-roll-p to permit "XY" as a gate name. To make this more ergonomic, there are then some other translators to write, like converting CONTROLLED XY into FORKED XY.

Dunno about the ISWAP version of the same change, since that compiler is also guarded by uniformly-controlled-roll-p. The easiest fix is to change the error in the cond to a try-next-compiler. Another thought, though, is to try dropping the ISWAP version entirely; since we began trampolining, I'm not sure we've (in the relevant era: I've) checked that the ISWAP-specific decomposition is giving something better than chaining UCR->CZ->ISWAP + compressor. If it isn't, let's drop it. If it is, then it shouldn't be hard to bang out the same hand-optimizations that went into building the old ISWAP decompositions, which are based off of the CZ->ISWAP translator anyway.

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

No branches or pull requests

2 participants