Skip to content

Commit

Permalink
Make optimize_1q_gates U and P aware (#5429)
Browse files Browse the repository at this point in the history
* Make optimize_1q_gates U aware

The optimize 1q gates pass currently only operates on u1, u2, u3 (or a
subset of that), however the u gate is interchangeable with the u3 gate
and the pass can be run if there is a u in the basis just as easily as
if it were a u3. This comes up for backends with overcomplete basis
sets, like Aer, which can have u and u3 in the same circuit. The
optimization pass will not simplify those gates despite it being
possible.

* Make Optimize1QGates Phase gate aware too

Just as U and U3 are interchangeable, U1 and P are also interchangeable.
This commit makes the same change to the Optimize1QGates pass done
earlier for supporting U for U1 and P so that if will work for a basis
where P is used instead of U1.

* Fix lint

Co-authored-by: Kevin Krsulich <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 26, 2020
1 parent f63d2cc commit 9e6a016
Show file tree
Hide file tree
Showing 2 changed files with 298 additions and 24 deletions.
80 changes: 57 additions & 23 deletions qiskit/transpiler/passes/optimization/optimize_1q_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import numpy as np

from qiskit.transpiler.exceptions import TranspilerError
from qiskit.circuit.library.standard_gates.p import PhaseGate
from qiskit.circuit.library.standard_gates.u import UGate
from qiskit.circuit.library.standard_gates.u1 import U1Gate
from qiskit.circuit.library.standard_gates.u2 import U2Gate
from qiskit.circuit.library.standard_gates.u3 import U3Gate
Expand All @@ -36,7 +38,7 @@ def __init__(self, basis=None, eps=1e-15):
Args:
basis (list[str]): Basis gates to consider, e.g. `['u3', 'cx']`. For the effects
of this pass, the basis is the set intersection between the `basis` parameter and
the set `{'u1','u2','u3'}`.
the set `{'u1','u2','u3', 'u', 'p'}`.
eps (float): EPS to check against
"""
super().__init__()
Expand All @@ -55,27 +57,35 @@ def run(self, dag):
Raises:
TranspilerError: if YZY and ZYZ angles do not give same rotation matrix.
"""
runs = dag.collect_runs(["u1", "u2", "u3"])
use_u = 'u' in self.basis
use_p = 'p' in self.basis
runs = dag.collect_runs(["u1", "u2", "u3", "u", 'p'])
runs = _split_runs_on_parameters(runs)
for run in runs:
right_name = "u1"
if use_p:
right_name = "p"
else:
right_name = "u1"
right_parameters = (0, 0, 0) # (theta, phi, lambda)
right_global_phase = 0
for current_node in run:
left_name = current_node.name
if (current_node.condition is not None
or len(current_node.qargs) != 1
or left_name not in ["u1", "u2", "u3", "id"]):
or left_name not in ["p", "u1", "u2", "u3", 'u', "id"]):
raise TranspilerError("internal error")
if left_name == "u1":
if left_name in ("u1", "p"):
left_parameters = (0, 0, current_node.op.params[0])
elif left_name == "u2":
left_parameters = (np.pi / 2, current_node.op.params[0],
current_node.op.params[1])
elif left_name == "u3":
elif left_name in ("u3", 'u'):
left_parameters = tuple(current_node.op.params)
else:
left_name = "u1" # replace id with u1
if use_p:
left_name = "p"
else:
left_name = "u1" # replace id with u1
left_parameters = (0, 0, 0)
if (current_node.op.definition is not None and
current_node.op.definition.global_phase):
Expand All @@ -85,36 +95,42 @@ def run(self, dag):
left_parameters = tuple([float(x) for x in left_parameters])
# Compose gates
name_tuple = (left_name, right_name)
if name_tuple == ("u1", "u1"):
if name_tuple in (("u1", "u1"), ("p", "p")):
# u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2)
right_parameters = (0, 0, right_parameters[2] +
left_parameters[2])
elif name_tuple == ("u1", "u2"):
elif name_tuple in (("u1", "u2"), ("p", "u2")):
# u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2)
right_parameters = (np.pi / 2, right_parameters[1] +
left_parameters[2], right_parameters[2])
elif name_tuple == ("u2", "u1"):
elif name_tuple in (("u2", "u1"), ("u2", "p")):
# u2(phi1, lambda1) * u1(lambda2) = u2(phi1, lambda1 + lambda2)
right_name = "u2"
right_parameters = (np.pi / 2, left_parameters[1],
right_parameters[2] + left_parameters[2])
elif name_tuple == ("u1", "u3"):
elif name_tuple in (("u1", "u3"), ("u1", "u"), ("p", "u3"), ("p", "u")):
# u1(lambda1) * u3(theta2, phi2, lambda2) =
# u3(theta2, phi2 + lambda1, lambda2)
right_parameters = (right_parameters[0], right_parameters[1] +
left_parameters[2], right_parameters[2])
elif name_tuple == ("u3", "u1"):
elif name_tuple in (("u3", "u1"), ('u', 'u1'), ("u3", "p"), ("u", "p")):
# u3(theta1, phi1, lambda1) * u1(lambda2) =
# u3(theta1, phi1, lambda1 + lambda2)
right_name = "u3"
if use_u:
right_name = 'u'
else:
right_name = "u3"
right_parameters = (left_parameters[0], left_parameters[1],
right_parameters[2] + left_parameters[2])
elif name_tuple == ("u2", "u2"):
# Using Ry(pi/2).Rz(2*lambda).Ry(pi/2) =
# Rz(pi/2).Ry(pi-2*lambda).Rz(pi/2),
# u2(phi1, lambda1) * u2(phi2, lambda2) =
# u3(pi - lambda1 - phi2, phi1 + pi/2, lambda2 + pi/2)
right_name = "u3"
if use_u:
right_name = 'u'
else:
right_name = "u3"
right_parameters = (np.pi - left_parameters[2] -
right_parameters[1], left_parameters[1] +
np.pi / 2, right_parameters[2] +
Expand All @@ -126,7 +142,10 @@ def run(self, dag):
# For composing u3's or u2's with u3's, use
# u2(phi, lambda) = u3(pi/2, phi, lambda)
# together with the qiskit.mapper.compose_u3 method.
right_name = "u3"
if use_u:
right_name = 'u'
else:
right_name = "u3"
# Evaluate the symbolic expressions for efficiency
right_parameters = Optimize1qGates.compose_u3(left_parameters[0],
left_parameters[1],
Expand Down Expand Up @@ -160,13 +179,17 @@ def run(self, dag):

# Y rotation is 0 mod 2*pi, so the gate is a u1
if abs(np.mod(right_parameters[0],
(2 * np.pi))) < self.eps and right_name != "u1":
right_name = "u1"
(2 * np.pi))) < self.eps and right_name != "u1" \
and right_name != "p":
if use_p:
right_name = "p"
else:
right_name = "u1"
right_parameters = (0, 0, right_parameters[1] +
right_parameters[2] +
right_parameters[0])
# Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2
if right_name == "u3":
if right_name == "u3" or 'u':
# theta = pi/2 + 2*k*pi
right_angle = right_parameters[0] - np.pi / 2
if abs(right_angle) < self.eps:
Expand All @@ -189,20 +212,31 @@ def run(self, dag):
np.pi + (right_parameters[0] +
np.pi / 2))
# u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase)
if right_name == "u1" and abs(np.mod(right_parameters[2],
2 * np.pi)) < self.eps:
if right_name in ("u1", "p") and abs(
np.mod(right_parameters[2], 2 * np.pi)) < self.eps:
right_name = "nop"

if right_name == "u2" and "u2" not in self.basis:
right_name = "u3"
if right_name == "u1" and "u1" not in self.basis:
right_name = "u3"
if use_u:
right_name = 'u'
else:
right_name = "u3"
if right_name in ("u1", "p") and right_name not in self.basis:
if use_u:
right_name = 'u'
else:
right_name = "u3"

new_op = Gate(name="", num_qubits=1, params=[])
if right_name == "u1":
new_op = U1Gate(right_parameters[2])
if right_name == "p":
new_op = PhaseGate(right_parameters[2])
if right_name == "u2":
new_op = U2Gate(right_parameters[1], right_parameters[2])
if right_name == "u":
if "u" in self.basis:
new_op = UGate(*right_parameters)
if right_name == "u3":
if "u3" in self.basis:
new_op = U3Gate(*right_parameters)
Expand Down
Loading

0 comments on commit 9e6a016

Please sign in to comment.