Skip to content

Commit

Permalink
Use native Gate.dagger() in Program.dagger()
Browse files Browse the repository at this point in the history
This removes a bunch of logic around creating inverse gates from
user-defined gates. If anyone thinks that stuff is useful, then I
would argue that it should be in some util function, not in
Program.dagger().

Kept kw args for backwards compat :(
  • Loading branch information
notmgsk committed Jun 6, 2019
1 parent 910dfe7 commit 13062df
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 86 deletions.
43 changes: 11 additions & 32 deletions pyquil/quil.py
Expand Up @@ -557,44 +557,23 @@ def pop(self):

def dagger(self, inv_dict=None, suffix="-INV"):
"""
Creates the conjugate transpose of the Quil program. The program must not
contain any irreversible actions (measurement, control flow, qubit allocation).
Creates the conjugate transpose of the Quil program. The program must
contain only gate applications.
Note: the keyword arguments inv_dict and suffix are kept only
for backwards compatibility and have no effect.
:return: The Quil program's inverse
:rtype: Program
"""
if not self.is_protoquil():
raise ValueError("Program must be valid Protoquil")

daggered = Program()

for gate in self._defined_gates:
if inv_dict is None or gate.name not in inv_dict:
if gate.parameters:
raise TypeError("Cannot auto define daggered version of parameterized gates")
daggered.defgate(gate.name + suffix, gate.matrix.T.conj())

for gate in reversed(self._instructions):
if gate.name in QUANTUM_GATES:
if gate.name == "S":
daggered.inst(QUANTUM_GATES["PHASE"](-pi / 2, *gate.qubits))
elif gate.name == "T":
daggered.inst(QUANTUM_GATES["RZ"](pi / 4, *gate.qubits))
elif gate.name == "ISWAP":
daggered.inst(QUANTUM_GATES["PSWAP"](pi / 2, *gate.qubits))
else:
negated_params = list(map(lambda x: -1 * x, gate.params))
daggered.inst(QUANTUM_GATES[gate.name](*(negated_params + gate.qubits)))
else:
if inv_dict is None or gate.name not in inv_dict:
gate_inv_name = gate.name + suffix
else:
gate_inv_name = inv_dict[gate.name]

daggered.inst(Gate(gate_inv_name, gate.params, gate.qubits))
if any(not isinstance(instr, Gate) for instr in self._instructions):
raise ValueError("Program to be daggered must contain only gate applications")

return daggered
# This is a bit hacky. Gate.dagger() mutates the gate object,
# rather than returning a fresh (and daggered) copy.
return Program([instr.dagger() for instr
in reversed(Program(self.out())._instructions)])

def _synthesize(self):
"""
Expand Down
60 changes: 6 additions & 54 deletions pyquil/tests/test_quil.py
Expand Up @@ -287,61 +287,13 @@ def test_measure_all():


def test_dagger():
# these gates are their own inverses
p = Program().inst(I(0), X(0), Y(0), Z(0),
H(0), CNOT(0, 1), CCNOT(0, 1, 2),
SWAP(0, 1), CSWAP(0, 1, 2))
assert p.dagger().out() == 'CSWAP 0 1 2\nSWAP 0 1\n' \
'CCNOT 0 1 2\nCNOT 0 1\nH 0\n' \
'Z 0\nY 0\nX 0\nI 0\n'

# these gates require negating a parameter
p = Program().inst(PHASE(pi, 0), RX(pi, 0), RY(pi, 0),
RZ(pi, 0), CPHASE(pi, 0, 1),
CPHASE00(pi, 0, 1), CPHASE01(pi, 0, 1),
CPHASE10(pi, 0, 1), PSWAP(pi, 0, 1))
assert p.dagger().out() == 'PSWAP(-pi) 0 1\n' \
'CPHASE10(-pi) 0 1\n' \
'CPHASE01(-pi) 0 1\n' \
'CPHASE00(-pi) 0 1\n' \
'CPHASE(-pi) 0 1\n' \
'RZ(-pi) 0\n' \
'RY(-pi) 0\n' \
'RX(-pi) 0\n' \
'PHASE(-pi) 0\n'

# these gates are special cases
p = Program().inst(S(0), T(0), ISWAP(0, 1))
assert p.dagger().out() == 'PSWAP(pi/2) 0 1\n' \
'RZ(pi/4) 0\n' \
'PHASE(-pi/2) 0\n'

# must invert defined gates
G = np.array([[0, 1], [0 + 1j, 0]])
p = Program().defgate("G", G).inst(("G", 0))
assert p.dagger().out() == 'DEFGATE G-INV:\n' \
' 0.0, -i\n' \
' 1.0, 0.0\n\n' \
'G-INV 0\n'

# can also pass in a list of inverses
inv_dict = {"G": "J"}
p = Program().defgate("G", G).inst(("G", 0))
assert p.dagger(inv_dict=inv_dict).out() == 'J 0\n'

# defined parameterized gates cannot auto generate daggered version https://github.com/rigetti/pyquil/issues/304
theta = Parameter('theta')
gparam_matrix = np.array([[quil_cos(theta / 2), -1j * quil_sin(theta / 2)],
[-1j * quil_sin(theta / 2), quil_cos(theta / 2)]])
g_param_def = DefGate('GPARAM', gparam_matrix, [theta])
p = Program(g_param_def)
with pytest.raises(TypeError):
p.dagger()
p = Program(X(0), H(0))
assert p.dagger().out() == 'DAGGER H 0\n' \
'DAGGER X 0\n'

# defined parameterized gates should passback parameters https://github.com/rigetti/pyquil/issues/304
GPARAM = g_param_def.get_constructor()
p = Program(GPARAM(pi)(1, 2))
assert p.dagger().out() == 'GPARAM-INV(pi) 1 2\n'
p = Program(X(0), MEASURE(0, 0))
with pytest.raises(ValueError) as e:
p.dagger().out()


def test_construction_syntax():
Expand Down

0 comments on commit 13062df

Please sign in to comment.