From 0b78d5934e0c0d2113508bfce0877e893d123ca9 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 12 May 2020 13:25:45 -0400 Subject: [PATCH 1/9] Add phase attribute to Gate --- qiskit/circuit/gate.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index a6debab861d4..165c6121a33f 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -14,6 +14,8 @@ """Unitary gate.""" +import math +import cmath from typing import List, Optional, Union, Tuple import numpy as np from scipy.linalg import schur @@ -26,6 +28,7 @@ class Gate(Instruction): """Unitary gate.""" def __init__(self, name: str, num_qubits: int, params: List, + phase: Optional[float] = 0, label: Optional[str] = None) -> None: """Create a new gate. @@ -33,12 +36,19 @@ def __init__(self, name: str, num_qubits: int, params: List, name: The Qobj name of the gate. num_qubits: The number of qubits the gate acts on. params: A list of parameters. + phase: Phase of the gate. label: An optional label for the gate. """ + self._phase = phase self._label = label self.definition = None super().__init__(name, num_qubits, 0, params) + def _matrix_definition(self): + """Return the canonical matrix definition of the gate with phase = 0.""" + # This should be set in classes that derive from Gate. + return None + def to_matrix(self) -> np.ndarray: """Return a Numpy.array for the gate unitary matrix. @@ -46,7 +56,11 @@ def to_matrix(self) -> np.ndarray: CircuitError: If a Gate subclass does not implement this method an exception will be raised when this base class method is called. """ - raise CircuitError("to_matrix not defined for this {}".format(type(self))) + # pylint: disable=assignment-from-none + mat = self._matrix_definition() + if mat is None: + raise CircuitError("to_matrix not defined for this {}".format(type(self))) + return cmath.exp(1j * self._phase) * mat if self._phase else mat def power(self, exponent: float): """Creates a unitary gate as `gate^exponent`. @@ -80,7 +94,7 @@ def power(self, exponent: float): def _return_repeat(self, exponent: float) -> 'Gate': return Gate(name="%s*%s" % (self.name, exponent), num_qubits=self.num_qubits, - params=self.params) + params=self.params, phase=exponent * self._phase) def assemble(self) -> 'Instruction': """Assemble a QasmQobjInstruction""" @@ -89,6 +103,23 @@ def assemble(self) -> 'Instruction': instruction.label = self.label return instruction + @property + def phase(self) -> float: + """Return the phase of the gate.""" + return self._phase + + @phase.setter + def phase(self, angle: float): + """Set the phase of the gate.""" + # Set the phase to the [-2 * pi, 2 * pi] interval + angle = float(angle) + if not angle: + self._phase = 0 + elif angle < 0: + self._phase = angle % (-2 * math.pi) + else: + self._phase = angle % (2 * math.pi) + @property def label(self) -> str: """Return gate label""" From 95767524d84e1847a864150a2b01ac2b7707dfef Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 12 May 2020 13:26:16 -0400 Subject: [PATCH 2/9] Update U1, U2, U3 with phase --- qiskit/circuit/library/standard_gates/u1.py | 16 ++++++---------- qiskit/circuit/library/standard_gates/u2.py | 16 +++++++--------- qiskit/circuit/library/standard_gates/u3.py | 10 +++++----- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/u1.py b/qiskit/circuit/library/standard_gates/u1.py index c3a083c2fc0f..4b58d3cd5827 100644 --- a/qiskit/circuit/library/standard_gates/u1.py +++ b/qiskit/circuit/library/standard_gates/u1.py @@ -75,20 +75,16 @@ class U1Gate(Gate): `1612.00858 `_ """ - def __init__(self, theta, label=None): + def __init__(self, theta, phase=0, label=None): """Create new U1 gate.""" - super().__init__('u1', 1, [theta], label=label) + super().__init__('u1', 1, [theta], phase=phase, label=label) def _define(self): from .u3 import U3Gate # pylint: disable=cyclic-import - definition = [] q = QuantumRegister(1, 'q') - rule = [ - (U3Gate(0, 0, self.params[0]), [q[0]], []) + self.definition = [ + (U3Gate(0, 0, self.params[0], phase=self.phase), [q[0]], []) ] - for inst in rule: - definition.append(inst) - self.definition = definition def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Return a (mutli-)controlled-U1 gate. @@ -114,9 +110,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): def inverse(self): r"""Return inverted U1 gate (:math:`U1(\lambda){\dagger} = U1(-\lambda)`)""" - return U1Gate(-self.params[0]) + return U1Gate(-self.params[0], phase=-self.phase) - def to_matrix(self): + def _matrix_definition(self): """Return a numpy.array for the U1 gate.""" lam = self.params[0] lam = float(lam) diff --git a/qiskit/circuit/library/standard_gates/u2.py b/qiskit/circuit/library/standard_gates/u2.py index aa34ba1ff5e7..75f05a6daf74 100644 --- a/qiskit/circuit/library/standard_gates/u2.py +++ b/qiskit/circuit/library/standard_gates/u2.py @@ -59,27 +59,25 @@ class U2Gate(Gate): using two X90 pulses. """ - def __init__(self, phi, lam, label=None): + def __init__(self, phi, lam, phase=0, label=None): """Create new U2 gate.""" - super().__init__('u2', 1, [phi, lam], label=label) + super().__init__('u2', 1, [phi, lam], phase=0, label=label) def _define(self): from .u3 import U3Gate - definition = [] q = QuantumRegister(1, 'q') - rule = [(U3Gate(pi / 2, self.params[0], self.params[1]), [q[0]], [])] - for inst in rule: - definition.append(inst) - self.definition = definition + self.definition = [ + (U3Gate(pi / 2, self.params[0], self.params[1], phase=self.phase), [q[0]], []) + ] def inverse(self): r"""Return inverted U2 gate. :math:`U2(\phi, \lambda)^{\dagger} =U2(-\lambda-\pi, -\phi+\pi)`) """ - return U2Gate(-self.params[1] - pi, -self.params[0] + pi) + return U2Gate(-self.params[1] - pi, -self.params[0] + pi, phase=-self.phase) - def to_matrix(self): + def _matrix_definition(self): """Return a Numpy.array for the U2 gate.""" isqrt2 = 1 / numpy.sqrt(2) phi, lam = self.params diff --git a/qiskit/circuit/library/standard_gates/u3.py b/qiskit/circuit/library/standard_gates/u3.py index 087355ab38f6..8447b19e8850 100644 --- a/qiskit/circuit/library/standard_gates/u3.py +++ b/qiskit/circuit/library/standard_gates/u3.py @@ -59,16 +59,16 @@ class U3Gate(Gate): U3(\theta, 0, 0) = RY(\theta) """ - def __init__(self, theta, phi, lam, label=None): + def __init__(self, theta, phi, lam, phase=0, label=None): """Create new U3 gate.""" - super().__init__('u3', 1, [theta, phi, lam], label=label) + super().__init__('u3', 1, [theta, phi, lam], phase=phase, label=label) def inverse(self): r"""Return inverted U3 gate. :math:`U3(\theta,\phi,\lambda)^{\dagger} =U3(-\theta,-\phi,-\lambda)`) """ - return U3Gate(-self.params[0], -self.params[2], -self.params[1]) + return U3Gate(-self.params[0], -self.params[2], -self.params[1], phase=-self.phase) def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Return a (mutli-)controlled-U3 gate. @@ -82,13 +82,13 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: + if num_ctrl_qubits == 1 and self.phase == 0: gate = CU3Gate(*self.params, label=label, ctrl_state=ctrl_state) gate.base_gate.label = self.label return gate return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) - def to_matrix(self): + def _matrix_definition(self): """Return a Numpy.array for the U3 gate.""" theta, phi, lam = self.params theta, phi, lam = float(theta), float(phi), float(lam) From 00a74fb376ae6de54f429e46a7c3cad5eced0544 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 12 May 2020 13:26:26 -0400 Subject: [PATCH 3/9] Update R, Rx, Ry, Rz with phase --- qiskit/circuit/library/standard_gates/r.py | 16 ++++------ qiskit/circuit/library/standard_gates/rx.py | 18 +++++------ qiskit/circuit/library/standard_gates/ry.py | 18 +++++------ qiskit/circuit/library/standard_gates/rz.py | 33 ++++++++++----------- 4 files changed, 35 insertions(+), 50 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/r.py b/qiskit/circuit/library/standard_gates/r.py index 342d124581bd..a439fbe0c9e7 100644 --- a/qiskit/circuit/library/standard_gates/r.py +++ b/qiskit/circuit/library/standard_gates/r.py @@ -24,34 +24,30 @@ class RGate(Gate): """Rotation θ around the cos(φ)x + sin(φ)y axis.""" - def __init__(self, theta, phi): + def __init__(self, theta, phi, phase=0): """Create new r single-qubit gate.""" - super().__init__('r', 1, [theta, phi]) + super().__init__('r', 1, [theta, phi], phase=phase) def _define(self): """ gate r(θ, φ) a {u3(θ, φ - π/2, -φ + π/2) a;} """ from .u3 import U3Gate - definition = [] q = QuantumRegister(1, 'q') theta = self.params[0] phi = self.params[1] - rule = [ - (U3Gate(theta, phi - pi / 2, -phi + pi / 2), [q[0]], []) + self.definition = [ + (U3Gate(theta, phi - pi / 2, -phi + pi / 2, phase=self.phase), [q[0]], []) ] - for inst in rule: - definition.append(inst) - self.definition = definition def inverse(self): """Invert this gate. r(θ, φ)^dagger = r(-θ, φ) """ - return RGate(-self.params[0], self.params[1]) + return RGate(-self.params[0], self.params[1], phase=-self.phase) - def to_matrix(self): + def _matrix_definition(self): """Return a numpy.array for the R gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) diff --git a/qiskit/circuit/library/standard_gates/rx.py b/qiskit/circuit/library/standard_gates/rx.py index 0023ef405291..2d3fe847b705 100644 --- a/qiskit/circuit/library/standard_gates/rx.py +++ b/qiskit/circuit/library/standard_gates/rx.py @@ -46,23 +46,19 @@ class RXGate(Gate): \end{pmatrix} """ - def __init__(self, theta, label=None): + def __init__(self, theta, phase=0, label=None): """Create new RX gate.""" - super().__init__('rx', 1, [theta], label=label) + super().__init__('rx', 1, [theta], phase=phase, label=label) def _define(self): """ gate rx(theta) a {r(theta, 0) a;} """ from .r import RGate - definition = [] q = QuantumRegister(1, 'q') - rule = [ - (RGate(self.params[0], 0), [q[0]], []) + self.definition = [ + (RGate(self.params[0], 0, phase=self.phase), [q[0]], []) ] - for inst in rule: - definition.append(inst) - self.definition = definition def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Return a (mutli-)controlled-RX gate. @@ -76,7 +72,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: + if num_ctrl_qubits == 1 and self.phase == 0: gate = CRXGate(self.params[0], label=label, ctrl_state=ctrl_state) gate.base_gate.label = self.label return gate @@ -87,9 +83,9 @@ def inverse(self): :math:`RX(\lambda)^{\dagger} = RX(-\lambda)` """ - return RXGate(-self.params[0]) + return RXGate(-self.params[0], phase=-self.phase) - def to_matrix(self): + def _matrix_definition(self): """Return a numpy.array for the RX gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) diff --git a/qiskit/circuit/library/standard_gates/ry.py b/qiskit/circuit/library/standard_gates/ry.py index ff9140c6e429..301693f561cc 100644 --- a/qiskit/circuit/library/standard_gates/ry.py +++ b/qiskit/circuit/library/standard_gates/ry.py @@ -46,23 +46,19 @@ class RYGate(Gate): \end{pmatrix} """ - def __init__(self, theta, label=None): + def __init__(self, theta, phase=0, label=None): """Create new RY gate.""" - super().__init__('ry', 1, [theta], label=label) + super().__init__('ry', 1, [theta], phase=phase, label=label) def _define(self): """ gate ry(theta) a { r(theta, pi/2) a; } """ from .r import RGate - definition = [] q = QuantumRegister(1, 'q') - rule = [ - (RGate(self.params[0], pi / 2), [q[0]], []) + self.definition = [ + (RGate(self.params[0], pi / 2, phase=self.phase), [q[0]], []) ] - for inst in rule: - definition.append(inst) - self.definition = definition def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Return a (mutli-)controlled-RY gate. @@ -76,7 +72,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: + if num_ctrl_qubits == 1 and self.phase == 0: gate = CRYGate(self.params[0], label=label, ctrl_state=ctrl_state) gate.base_gate.label = self.label return gate @@ -87,9 +83,9 @@ def inverse(self): :math:`RY(\lambda){\dagger} = RY(-\lambda)` """ - return RYGate(-self.params[0]) + return RYGate(-self.params[0], phase=-self.phase) - def to_matrix(self): + def _matrix_definition(self): """Return a numpy.array for the RY gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) diff --git a/qiskit/circuit/library/standard_gates/rz.py b/qiskit/circuit/library/standard_gates/rz.py index d55bfc449c62..b89d99fbf3b4 100644 --- a/qiskit/circuit/library/standard_gates/rz.py +++ b/qiskit/circuit/library/standard_gates/rz.py @@ -14,6 +14,8 @@ """Rotation around the Z axis.""" +import numpy as np + from qiskit.circuit.gate import Gate from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.quantumregister import QuantumRegister @@ -56,23 +58,20 @@ class RZGate(Gate): `1612.00858 `_ """ - def __init__(self, phi, label=None): + def __init__(self, phi, phase=0, label=None): """Create new RZ gate.""" - super().__init__('rz', 1, [phi], label=label) + super().__init__('rz', 1, [phi], phase=phase, label=label) def _define(self): """ gate rz(phi) a { u1(phi) a; } """ from .u1 import U1Gate - definition = [] q = QuantumRegister(1, 'q') - rule = [ - (U1Gate(self.params[0]), [q[0]], []) + phase = self.phase + 0.5 * float(self.params[0]) + self.definition = [ + (U1Gate(self.params[0], phase=phase), [q[0]], []) ] - for inst in rule: - definition.append(inst) - self.definition = definition def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Return a (mutli-)controlled-RZ gate. @@ -86,7 +85,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): Returns: ControlledGate: controlled version of this gate. """ - if num_ctrl_qubits == 1: + if num_ctrl_qubits == 1 and self.phase == 0: gate = CRZGate(self.params[0], label=label, ctrl_state=ctrl_state) gate.base_gate.label = self.label return gate @@ -97,15 +96,13 @@ def inverse(self): :math:`RZ(\lambda){\dagger} = RZ(-\lambda)` """ - return RZGate(-self.params[0]) - - # TODO: this is the correct matrix however the control mechanism - # cannot distinguish U1 and RZ yet. - # def to_matrix(self): - # """Return a numpy.array for the RZ gate.""" - # lam = float(self.params[0]) - # return np.array([[np.exp(-1j * lam / 2), 0], - # [0, np.exp(1j * lam / 2)]], dtype=complex) + return RZGate(-self.params[0], phase=-self.phase) + + def _matrix_definition(self): + """Return a numpy.array for the RZ gate.""" + lam = float(self.params[0]) + return np.array([[np.exp(-1j * lam / 2), 0], + [0, np.exp(1j * lam / 2)]], dtype=complex) class CRZMeta(type): From 049161ce3a50c9c3358648002aa11d594561fbdf Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 12 May 2020 13:27:59 -0400 Subject: [PATCH 4/9] Update OneQubitEulerDecomposer to be phase correct --- .../synthesis/one_qubit_decompose.py | 32 +++++++++++-------- test/python/quantum_info/test_synthesis.py | 2 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/qiskit/quantum_info/synthesis/one_qubit_decompose.py b/qiskit/quantum_info/synthesis/one_qubit_decompose.py index fa681af2bd41..281226373eab 100644 --- a/qiskit/quantum_info/synthesis/one_qubit_decompose.py +++ b/qiskit/quantum_info/synthesis/one_qubit_decompose.py @@ -119,8 +119,8 @@ def __call__(self, if not is_unitary_matrix(unitary): raise QiskitError("OneQubitEulerDecomposer: " "input matrix is not unitary.") - theta, phi, lam, _ = self._params(unitary) - circuit = self._circuit(theta, phi, lam, + theta, phi, lam, phase = self._params(unitary) + circuit = self._circuit(theta, phi, lam, phase=phase, simplify=simplify, atol=atol) return circuit @@ -236,16 +236,17 @@ def _params_u1x(mat): def _circuit_zyz(theta, phi, lam, + phase=0, simplify=True, atol=DEFAULT_ATOL): circuit = QuantumCircuit(1) if simplify and np.isclose(theta, 0.0, atol=atol): - circuit.append(RZGate(phi + lam), [0]) + circuit.append(RZGate(phi + lam, phase=phase), [0]) return circuit if not simplify or not np.isclose(lam, 0.0, atol=atol): circuit.append(RZGate(lam), [0]) if not simplify or not np.isclose(theta, 0.0, atol=atol): - circuit.append(RYGate(theta), [0]) + circuit.append(RYGate(theta, phase=phase), [0]) if not simplify or not np.isclose(phi, 0.0, atol=atol): circuit.append(RZGate(phi), [0]) return circuit @@ -254,17 +255,18 @@ def _circuit_zyz(theta, def _circuit_zxz(theta, phi, lam, + phase=0, simplify=False, atol=DEFAULT_ATOL): if simplify and np.isclose(theta, 0.0, atol=atol): circuit = QuantumCircuit(1) - circuit.append(RZGate(phi + lam), [0]) + circuit.append(RZGate(phi + lam, phase=phase), [0]) return circuit circuit = QuantumCircuit(1) if not simplify or not np.isclose(lam, 0.0, atol=atol): circuit.append(RZGate(lam), [0]) if not simplify or not np.isclose(theta, 0.0, atol=atol): - circuit.append(RXGate(theta), [0]) + circuit.append(RXGate(theta, phase=phase), [0]) if not simplify or not np.isclose(phi, 0.0, atol=atol): circuit.append(RZGate(phi), [0]) return circuit @@ -273,16 +275,17 @@ def _circuit_zxz(theta, def _circuit_xyx(theta, phi, lam, + phase=0, simplify=True, atol=DEFAULT_ATOL): circuit = QuantumCircuit(1) if simplify and np.isclose(theta, 0.0, atol=atol): - circuit.append(RXGate(phi + lam), [0]) + circuit.append(RXGate(phi + lam, phase=phase), [0]) return circuit if not simplify or not np.isclose(lam, 0.0, atol=atol): circuit.append(RXGate(lam), [0]) if not simplify or not np.isclose(theta, 0.0, atol=atol): - circuit.append(RYGate(theta), [0]) + circuit.append(RYGate(theta, phase=phase), [0]) if not simplify or not np.isclose(phi, 0.0, atol=atol): circuit.append(RXGate(phi), [0]) return circuit @@ -291,17 +294,19 @@ def _circuit_xyx(theta, def _circuit_u3(theta, phi, lam, + phase=0, simplify=True, atol=DEFAULT_ATOL): # pylint: disable=unused-argument circuit = QuantumCircuit(1) - circuit.append(U3Gate(theta, phi, lam), [0]) + circuit.append(U3Gate(theta, phi, lam, phase=phase), [0]) return circuit @staticmethod def _circuit_u1x(theta, phi, lam, + phase=0, simplify=True, atol=DEFAULT_ATOL): # Shift theta and phi so decomposition is @@ -312,18 +317,18 @@ def _circuit_u1x(theta, if simplify and np.isclose(abs(theta), np.pi, atol=atol): # Zero X90 gate decomposition circuit = QuantumCircuit(1) - circuit.append(U1Gate(lam + phi + theta), [0]) + circuit.append(U1Gate(lam + phi + theta, phase=phase), [0]) return circuit if simplify and np.isclose(abs(theta), np.pi/2, atol=atol): # Single X90 gate decomposition circuit = QuantumCircuit(1) - circuit.append(U1Gate(lam + theta), [0]) + circuit.append(U1Gate(lam + theta, phase=phase), [0]) circuit.append(RXGate(np.pi / 2), [0]) circuit.append(U1Gate(phi + theta), [0]) return circuit # General two-X90 gate decomposition circuit = QuantumCircuit(1) - circuit.append(U1Gate(lam), [0]) + circuit.append(U1Gate(lam, phase=phase), [0]) circuit.append(RXGate(np.pi / 2), [0]) circuit.append(U1Gate(theta), [0]) circuit.append(RXGate(np.pi / 2), [0]) @@ -334,10 +339,11 @@ def _circuit_u1x(theta, def _circuit_rr(theta, phi, lam, + phase=0, simplify=True, atol=DEFAULT_ATOL): circuit = QuantumCircuit(1) if not simplify or not np.isclose(theta, -np.pi, atol=atol): circuit.append(RGate(theta + np.pi, np.pi / 2 - lam), [0]) - circuit.append(RGate(-np.pi, 0.5 * (phi - lam + np.pi)), [0]) + circuit.append(RGate(-np.pi, 0.5 * (phi - lam + np.pi), phase=phase), [0]) return circuit diff --git a/test/python/quantum_info/test_synthesis.py b/test/python/quantum_info/test_synthesis.py index b2226cf63f01..5f365ae4a3ae 100644 --- a/test/python/quantum_info/test_synthesis.py +++ b/test/python/quantum_info/test_synthesis.py @@ -114,7 +114,7 @@ class TestOneQubitEulerDecomposer(QiskitTestCase): def check_one_qubit_euler_angles(self, operator, basis='U3', tolerance=1e-12, - phase_equal=False): + phase_equal=True): """Check euler_angles_1q works for the given unitary""" decomposer = OneQubitEulerDecomposer(basis) with self.subTest(operator=operator): From 57aea609b3686089837a1b0f5c7bbf96188448f9 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 12 May 2020 13:53:07 -0400 Subject: [PATCH 5/9] Update RXX, RYY, RZZ, RZX gates with phase --- qiskit/circuit/library/standard_gates/rxx.py | 39 +++++++++----------- qiskit/circuit/library/standard_gates/ryy.py | 36 +++++++----------- qiskit/circuit/library/standard_gates/rz.py | 2 +- qiskit/circuit/library/standard_gates/rzx.py | 30 +++++++-------- qiskit/circuit/library/standard_gates/rzz.py | 36 ++++++++---------- 5 files changed, 63 insertions(+), 80 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/rxx.py b/qiskit/circuit/library/standard_gates/rxx.py index 273afe1397b0..46d633f83a0f 100644 --- a/qiskit/circuit/library/standard_gates/rxx.py +++ b/qiskit/circuit/library/standard_gates/rxx.py @@ -14,6 +14,8 @@ """Two-qubit XX-rotation gate.""" +import numpy as np + from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister @@ -68,43 +70,36 @@ class RXXGate(Gate): \end{pmatrix} """ - def __init__(self, theta): + def __init__(self, theta, phase=0): """Create new RXX gate.""" - super().__init__('rxx', 2, [theta]) + super().__init__('rxx', 2, [theta], phase=phase) def _define(self): """Calculate a subcircuit that implements this unitary.""" from .x import CXGate - from .u1 import U1Gate + from .rz import RZGate from .h import HGate - definition = [] q = QuantumRegister(2, 'q') theta = self.params[0] - rule = [ + self.definition = [ (HGate(), [q[0]], []), (HGate(), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (U1Gate(theta), [q[1]], []), + (RZGate(theta, phase=self.phase), [q[1]], []), (CXGate(), [q[0], q[1]], []), (HGate(), [q[1]], []), (HGate(), [q[0]], []), ] - for inst in rule: - definition.append(inst) - self.definition = definition def inverse(self): """Return inverse RXX gate (i.e. with the negative rotation angle).""" - return RXXGate(-self.params[0]) - - # NOTE: we should use the following as the canonical matrix - # definition but we don't include it yet since it differs from - # the circuit decomposition matrix by a global phase - # def to_matrix(self): - # """Return a Numpy.array for the RXX gate.""" - # theta = float(self.params[0]) - # return np.array([ - # [np.cos(theta / 2), 0, 0, -1j * np.sin(theta / 2)], - # [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], - # [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], - # [-1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)]], dtype=complex) + return RXXGate(-self.params[0], phase=-self.phase) + + def _matrix_definition(self): + """Return a Numpy.array for the RXX gate.""" + theta = float(self.params[0]) + return np.array([ + [np.cos(theta / 2), 0, 0, -1j * np.sin(theta / 2)], + [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], + [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], + [-1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)]], dtype=complex) diff --git a/qiskit/circuit/library/standard_gates/ryy.py b/qiskit/circuit/library/standard_gates/ryy.py index b039b0fa4d6b..80fc2f0cc2cf 100644 --- a/qiskit/circuit/library/standard_gates/ryy.py +++ b/qiskit/circuit/library/standard_gates/ryy.py @@ -69,44 +69,36 @@ class RYYGate(Gate): \end{pmatrix} """ - def __init__(self, theta): + def __init__(self, theta, phase=0): """Create new RYY gate.""" - super().__init__('ryy', 2, [theta]) + super().__init__('ryy', 2, [theta], phase=phase) def _define(self): """Calculate a subcircuit that implements this unitary.""" from .x import CXGate from .rx import RXGate from .rz import RZGate - - definition = [] q = QuantumRegister(2, 'q') theta = self.params[0] - rule = [ + self.definition = [ (RXGate(np.pi / 2), [q[0]], []), (RXGate(np.pi / 2), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (RZGate(theta), [q[1]], []), + (RZGate(theta, phase=self.phase), [q[1]], []), (CXGate(), [q[0], q[1]], []), (RXGate(-np.pi / 2), [q[0]], []), (RXGate(-np.pi / 2), [q[1]], []), ] - for inst in rule: - definition.append(inst) - self.definition = definition def inverse(self): """Return inverse RYY gate (i.e. with the negative rotation angle).""" - return RYYGate(-self.params[0]) - - # TODO: this is the correct matrix and is equal to the definition above, - # however the control mechanism cannot distinguish U1 and RZ yet. - # def to_matrix(self): - # """Return a numpy.array for the RYY gate.""" - # theta = self.params[0] - # return np.exp(0.5j * theta) * np.array([ - # [np.cos(theta / 2), 0, 0, 1j * np.sin(theta / 2)], - # [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], - # [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], - # [1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)] - # ], dtype=complex) + return RYYGate(-self.params[0], phase=-self.phase) + + def _matrix_definition(self): + """Return a numpy.array for the RYY gate.""" + theta = self.params[0] + return np.array([ + [np.cos(theta / 2), 0, 0, 1j * np.sin(theta / 2)], + [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], + [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], + [1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)]], dtype=complex) diff --git a/qiskit/circuit/library/standard_gates/rz.py b/qiskit/circuit/library/standard_gates/rz.py index b89d99fbf3b4..5c613ead205c 100644 --- a/qiskit/circuit/library/standard_gates/rz.py +++ b/qiskit/circuit/library/standard_gates/rz.py @@ -68,7 +68,7 @@ def _define(self): """ from .u1 import U1Gate q = QuantumRegister(1, 'q') - phase = self.phase + 0.5 * float(self.params[0]) + phase = self.phase - 0.5 * float(self.params[0]) self.definition = [ (U1Gate(self.params[0], phase=phase), [q[0]], []) ] diff --git a/qiskit/circuit/library/standard_gates/rzx.py b/qiskit/circuit/library/standard_gates/rzx.py index 8ae3da4caa6c..e068f849bc22 100644 --- a/qiskit/circuit/library/standard_gates/rzx.py +++ b/qiskit/circuit/library/standard_gates/rzx.py @@ -14,6 +14,8 @@ """Two-qubit ZX-rotation gate.""" +import numpy as np + from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from .rz import RZGate @@ -117,9 +119,9 @@ class RZXGate(Gate): \end{pmatrix} """ - def __init__(self, theta): + def __init__(self, theta, phase=0): """Create new RZX gate.""" - super().__init__('rzx', 2, [theta]) + super().__init__('rzx', 2, [theta], phase=phase) def _define(self): """ @@ -129,22 +131,20 @@ def _define(self): self.definition = [ (HGate(), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (RZGate(self.params[0]), [q[1]], []), + (RZGate(self.params[0], phase=self.phase), [q[1]], []), (CXGate(), [q[0], q[1]], []), (HGate(), [q[1]], []) ] def inverse(self): """Return inverse RZX gate (i.e. with the negative rotation angle).""" - return RZXGate(-self.params[0]) - - # TODO: this is the correct definition but has a global phase with respect - # to the decomposition above. Restore after allowing phase on circuits. - # def to_matrix(self): - # """Return a numpy.array for the RZX gate.""" - # theta = self.params[0] - # return np.array([[np.cos(theta/2), 0, -1j*np.sin(theta/2), 0], - # [0, np.cos(theta/2), 0, 1j*np.sin(theta/2)], - # [-1j*np.sin(theta/2), 0, np.cos(theta/2), 0], - # [0, 1j*np.sin(theta/2), 0, np.cos(theta/2)]], - # dtype=complex) + return RZXGate(-self.params[0], phase=-self.phase) + + def _matrix_definition(self): + """Return a numpy.array for the RZX gate.""" + theta = self.params[0] + return np.array([[np.cos(theta/2), 0, -1j*np.sin(theta/2), 0], + [0, np.cos(theta/2), 0, 1j*np.sin(theta/2)], + [-1j*np.sin(theta/2), 0, np.cos(theta/2), 0], + [0, 1j*np.sin(theta/2), 0, np.cos(theta/2)]], + dtype=complex) diff --git a/qiskit/circuit/library/standard_gates/rzz.py b/qiskit/circuit/library/standard_gates/rzz.py index cfb75b169559..579f8c891237 100644 --- a/qiskit/circuit/library/standard_gates/rzz.py +++ b/qiskit/circuit/library/standard_gates/rzz.py @@ -14,6 +14,8 @@ """Two-qubit ZZ-rotation gate.""" +import numpy as np + from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister @@ -81,37 +83,31 @@ class RZZGate(Gate): \end{pmatrix} """ - def __init__(self, theta): + def __init__(self, theta, phase=0): """Create new RZZ gate.""" - super().__init__('rzz', 2, [theta]) + super().__init__('rzz', 2, [theta], phase=phase) def _define(self): """ gate rzz(theta) a, b { cx a, b; u1(theta) b; cx a, b; } """ - from .u1 import U1Gate + from .rz import RZGate from .x import CXGate - definition = [] q = QuantumRegister(2, 'q') - rule = [ + self.definition = [ (CXGate(), [q[0], q[1]], []), - (U1Gate(self.params[0]), [q[1]], []), + (RZGate(self.params[0], phase=self.phase), [q[1]], []), (CXGate(), [q[0], q[1]], []) ] - for inst in rule: - definition.append(inst) - self.definition = definition def inverse(self): """Return inverse RZZ gate (i.e. with the negative rotation angle).""" - return RZZGate(-self.params[0]) - - # TODO: this is the correct matrix and is equal to the definition above, - # however the control mechanism cannot distinguish U1 and RZ yet. - # def to_matrix(self): - # """Return a numpy.array for the RZZ gate.""" - # theta = float(self.params[0]) - # return np.array([[np.exp(-1j*theta/2), 0, 0, 0], - # [0, np.exp(1j*theta/2), 0, 0], - # [0, 0, np.exp(1j*theta/2), 0], - # [0, 0, 0, np.exp(-1j*theta/2)]], dtype=complex) + return RZZGate(-self.params[0], phase=-self.phase) + + def _matrix_definition(self): + """Return a numpy.array for the RZZ gate.""" + theta = float(self.params[0]) + return np.array([[np.exp(-1j*theta/2), 0, 0, 0], + [0, np.exp(1j*theta/2), 0, 0], + [0, 0, np.exp(1j*theta/2), 0], + [0, 0, 0, np.exp(-1j*theta/2)]], dtype=complex) From 891fed6ba4af4a6aec612697bf89b554b8e8570e Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 12 May 2020 14:40:08 -0400 Subject: [PATCH 6/9] Handle case of ParameterExpression for phase --- qiskit/circuit/gate.py | 7 ++++++- qiskit/circuit/library/standard_gates/rz.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 165c6121a33f..6d88b656579e 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -60,7 +60,12 @@ def to_matrix(self) -> np.ndarray: mat = self._matrix_definition() if mat is None: raise CircuitError("to_matrix not defined for this {}".format(type(self))) - return cmath.exp(1j * self._phase) * mat if self._phase else mat + # Call float to enable conversion of bound ParameterExpressions + phase = float(self._phase) + if phase == 0: + # Case for default value to avoid numerical error + return mat + return cmath.exp(1j * phase) * mat def power(self, exponent: float): """Creates a unitary gate as `gate^exponent`. diff --git a/qiskit/circuit/library/standard_gates/rz.py b/qiskit/circuit/library/standard_gates/rz.py index 5c613ead205c..ea54583742ea 100644 --- a/qiskit/circuit/library/standard_gates/rz.py +++ b/qiskit/circuit/library/standard_gates/rz.py @@ -68,7 +68,7 @@ def _define(self): """ from .u1 import U1Gate q = QuantumRegister(1, 'q') - phase = self.phase - 0.5 * float(self.params[0]) + phase = self.phase - 0.5 * self.params[0] self.definition = [ (U1Gate(self.params[0], phase=phase), [q[0]], []) ] From bfdbac3f9e0996605564291f846317fb5fed382b Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 12 May 2020 15:13:52 -0400 Subject: [PATCH 7/9] Linting --- qiskit/circuit/gate.py | 6 ++++-- qiskit/circuit/library/standard_gates/rxx.py | 9 ++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 6d88b656579e..b01e27c27020 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -52,9 +52,11 @@ def _matrix_definition(self): def to_matrix(self) -> np.ndarray: """Return a Numpy.array for the gate unitary matrix. + Returns: + np.ndarray: the matrix representaiton of the gate. + Raises: - CircuitError: If a Gate subclass does not implement this method an - exception will be raised when this base class method is called. + CircuitError: If a Gate subclass does not have a _matrix_definition. """ # pylint: disable=assignment-from-none mat = self._matrix_definition() diff --git a/qiskit/circuit/library/standard_gates/rxx.py b/qiskit/circuit/library/standard_gates/rxx.py index 46d633f83a0f..899d890c79e9 100644 --- a/qiskit/circuit/library/standard_gates/rxx.py +++ b/qiskit/circuit/library/standard_gates/rxx.py @@ -98,8 +98,7 @@ def inverse(self): def _matrix_definition(self): """Return a Numpy.array for the RXX gate.""" theta = float(self.params[0]) - return np.array([ - [np.cos(theta / 2), 0, 0, -1j * np.sin(theta / 2)], - [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], - [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], - [-1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)]], dtype=complex) + return np.array([[np.cos(theta / 2), 0, 0, -1j * np.sin(theta / 2)], + [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], + [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], + [-1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)]], dtype=complex) From 7ed4c03b41d38db7ae0bdddf27274e55515af5b3 Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 12 May 2020 15:19:25 -0400 Subject: [PATCH 8/9] Revert adding phase to two-qubit pauli rotation gates These gates current fail control gate tests if they are made to be phase correct since they should have a RZGate in their definition. To avoid these test failures I have reverted them to using a U1Gate in their definition, which gives an incorrect phase on decomposition, but passes the current tests. This should be addressed and fixed in a follow up PR. --- qiskit/circuit/library/standard_gates/rxx.py | 38 +++++++++++--------- qiskit/circuit/library/standard_gates/ryy.py | 38 ++++++++++++-------- qiskit/circuit/library/standard_gates/rzx.py | 32 ++++++++--------- qiskit/circuit/library/standard_gates/rzz.py | 36 ++++++++++--------- 4 files changed, 81 insertions(+), 63 deletions(-) diff --git a/qiskit/circuit/library/standard_gates/rxx.py b/qiskit/circuit/library/standard_gates/rxx.py index 899d890c79e9..cd7c88de00f4 100644 --- a/qiskit/circuit/library/standard_gates/rxx.py +++ b/qiskit/circuit/library/standard_gates/rxx.py @@ -14,8 +14,6 @@ """Two-qubit XX-rotation gate.""" -import numpy as np - from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister @@ -70,35 +68,43 @@ class RXXGate(Gate): \end{pmatrix} """ - def __init__(self, theta, phase=0): + def __init__(self, theta): """Create new RXX gate.""" - super().__init__('rxx', 2, [theta], phase=phase) + super().__init__('rxx', 2, [theta]) def _define(self): """Calculate a subcircuit that implements this unitary.""" from .x import CXGate - from .rz import RZGate + from .u1 import U1Gate from .h import HGate + definition = [] q = QuantumRegister(2, 'q') theta = self.params[0] - self.definition = [ + rule = [ (HGate(), [q[0]], []), (HGate(), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (RZGate(theta, phase=self.phase), [q[1]], []), + (U1Gate(theta), [q[1]], []), # Should be RZGate (CXGate(), [q[0], q[1]], []), (HGate(), [q[1]], []), (HGate(), [q[0]], []), ] + for inst in rule: + definition.append(inst) + self.definition = definition def inverse(self): """Return inverse RXX gate (i.e. with the negative rotation angle).""" - return RXXGate(-self.params[0], phase=-self.phase) - - def _matrix_definition(self): - """Return a Numpy.array for the RXX gate.""" - theta = float(self.params[0]) - return np.array([[np.cos(theta / 2), 0, 0, -1j * np.sin(theta / 2)], - [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], - [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], - [-1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)]], dtype=complex) + return RXXGate(-self.params[0]) + + # NOTE: we should use the following as the canonical matrix + # definition but we don't include it yet since it differs from + # the circuit decomposition matrix by a global phase + # def to_matrix(self): + # """Return a Numpy.array for the RXX gate.""" + # theta = float(self.params[0]) + # return np.array([ + # [np.cos(theta / 2), 0, 0, -1j * np.sin(theta / 2)], + # [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], + # [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], + # [-1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)]], dtype=complex) diff --git a/qiskit/circuit/library/standard_gates/ryy.py b/qiskit/circuit/library/standard_gates/ryy.py index 80fc2f0cc2cf..570e23a41221 100644 --- a/qiskit/circuit/library/standard_gates/ryy.py +++ b/qiskit/circuit/library/standard_gates/ryy.py @@ -69,36 +69,44 @@ class RYYGate(Gate): \end{pmatrix} """ - def __init__(self, theta, phase=0): + def __init__(self, theta): """Create new RYY gate.""" - super().__init__('ryy', 2, [theta], phase=phase) + super().__init__('ryy', 2, [theta]) def _define(self): """Calculate a subcircuit that implements this unitary.""" from .x import CXGate from .rx import RXGate - from .rz import RZGate + from .u1 import U1Gate + + definition = [] q = QuantumRegister(2, 'q') theta = self.params[0] - self.definition = [ + rule = [ (RXGate(np.pi / 2), [q[0]], []), (RXGate(np.pi / 2), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (RZGate(theta, phase=self.phase), [q[1]], []), + (U1Gate(theta), [q[1]], []), # Should be RZGate (CXGate(), [q[0], q[1]], []), (RXGate(-np.pi / 2), [q[0]], []), (RXGate(-np.pi / 2), [q[1]], []), ] + for inst in rule: + definition.append(inst) + self.definition = definition def inverse(self): """Return inverse RYY gate (i.e. with the negative rotation angle).""" - return RYYGate(-self.params[0], phase=-self.phase) - - def _matrix_definition(self): - """Return a numpy.array for the RYY gate.""" - theta = self.params[0] - return np.array([ - [np.cos(theta / 2), 0, 0, 1j * np.sin(theta / 2)], - [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], - [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], - [1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)]], dtype=complex) + return RYYGate(-self.params[0]) + + # TODO: this is the correct matrix and is equal to the definition above, + # however the control mechanism cannot distinguish U1 and RZ yet. + # def to_matrix(self): + # """Return a numpy.array for the RYY gate.""" + # theta = self.params[0] + # return np.exp(0.5j * theta) * np.array([ + # [np.cos(theta / 2), 0, 0, 1j * np.sin(theta / 2)], + # [0, np.cos(theta / 2), -1j * np.sin(theta / 2), 0], + # [0, -1j * np.sin(theta / 2), np.cos(theta / 2), 0], + # [1j * np.sin(theta / 2), 0, 0, np.cos(theta / 2)] + # ], dtype=complex) diff --git a/qiskit/circuit/library/standard_gates/rzx.py b/qiskit/circuit/library/standard_gates/rzx.py index e068f849bc22..37985b7694f6 100644 --- a/qiskit/circuit/library/standard_gates/rzx.py +++ b/qiskit/circuit/library/standard_gates/rzx.py @@ -14,11 +14,9 @@ """Two-qubit ZX-rotation gate.""" -import numpy as np - from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from .rz import RZGate +from .u1 import U1Gate from .h import HGate from .x import CXGate @@ -119,9 +117,9 @@ class RZXGate(Gate): \end{pmatrix} """ - def __init__(self, theta, phase=0): + def __init__(self, theta): """Create new RZX gate.""" - super().__init__('rzx', 2, [theta], phase=phase) + super().__init__('rzx', 2, [theta]) def _define(self): """ @@ -131,20 +129,22 @@ def _define(self): self.definition = [ (HGate(), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (RZGate(self.params[0], phase=self.phase), [q[1]], []), + (U1Gate(self.params[0]), [q[1]], []), # Should be RZGate (CXGate(), [q[0], q[1]], []), (HGate(), [q[1]], []) ] def inverse(self): """Return inverse RZX gate (i.e. with the negative rotation angle).""" - return RZXGate(-self.params[0], phase=-self.phase) - - def _matrix_definition(self): - """Return a numpy.array for the RZX gate.""" - theta = self.params[0] - return np.array([[np.cos(theta/2), 0, -1j*np.sin(theta/2), 0], - [0, np.cos(theta/2), 0, 1j*np.sin(theta/2)], - [-1j*np.sin(theta/2), 0, np.cos(theta/2), 0], - [0, 1j*np.sin(theta/2), 0, np.cos(theta/2)]], - dtype=complex) + return RZXGate(-self.params[0]) + + # TODO: this is the correct definition but has a global phase with respect + # to the decomposition above. Restore after allowing phase on circuits. + # def to_matrix(self): + # """Return a numpy.array for the RZX gate.""" + # theta = self.params[0] + # return np.array([[np.cos(theta/2), 0, -1j*np.sin(theta/2), 0], + # [0, np.cos(theta/2), 0, 1j*np.sin(theta/2)], + # [-1j*np.sin(theta/2), 0, np.cos(theta/2), 0], + # [0, 1j*np.sin(theta/2), 0, np.cos(theta/2)]], + # dtype=complex) diff --git a/qiskit/circuit/library/standard_gates/rzz.py b/qiskit/circuit/library/standard_gates/rzz.py index 579f8c891237..77a7a5ee7e50 100644 --- a/qiskit/circuit/library/standard_gates/rzz.py +++ b/qiskit/circuit/library/standard_gates/rzz.py @@ -14,8 +14,6 @@ """Two-qubit ZZ-rotation gate.""" -import numpy as np - from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister @@ -83,31 +81,37 @@ class RZZGate(Gate): \end{pmatrix} """ - def __init__(self, theta, phase=0): + def __init__(self, theta): """Create new RZZ gate.""" - super().__init__('rzz', 2, [theta], phase=phase) + super().__init__('rzz', 2, [theta]) def _define(self): """ gate rzz(theta) a, b { cx a, b; u1(theta) b; cx a, b; } """ - from .rz import RZGate + from .u1 import U1Gate from .x import CXGate + definition = [] q = QuantumRegister(2, 'q') - self.definition = [ + rule = [ (CXGate(), [q[0], q[1]], []), - (RZGate(self.params[0], phase=self.phase), [q[1]], []), + (U1Gate(self.params[0]), [q[1]], []), # Should be RZGate (CXGate(), [q[0], q[1]], []) ] + for inst in rule: + definition.append(inst) + self.definition = definition def inverse(self): """Return inverse RZZ gate (i.e. with the negative rotation angle).""" - return RZZGate(-self.params[0], phase=-self.phase) - - def _matrix_definition(self): - """Return a numpy.array for the RZZ gate.""" - theta = float(self.params[0]) - return np.array([[np.exp(-1j*theta/2), 0, 0, 0], - [0, np.exp(1j*theta/2), 0, 0], - [0, 0, np.exp(1j*theta/2), 0], - [0, 0, 0, np.exp(-1j*theta/2)]], dtype=complex) + return RZZGate(-self.params[0]) + + # TODO: this is the correct matrix and is equal to the definition above, + # however the control mechanism cannot distinguish U1 and RZ yet. + # def to_matrix(self): + # """Return a numpy.array for the RZZ gate.""" + # theta = float(self.params[0]) + # return np.array([[np.exp(-1j*theta/2), 0, 0, 0], + # [0, np.exp(1j*theta/2), 0, 0], + # [0, 0, np.exp(1j*theta/2), 0], + # [0, 0, 0, np.exp(-1j*theta/2)]], dtype=complex) From d8b7b43defbfaa56fe4f4fd7d8f1c4bec3d10d4f Mon Sep 17 00:00:00 2001 From: Christopher Wood Date: Tue, 12 May 2020 15:50:34 -0400 Subject: [PATCH 9/9] Add release note --- .../notes/gate-phase-ff6549a85b545638.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 releasenotes/notes/gate-phase-ff6549a85b545638.yaml diff --git a/releasenotes/notes/gate-phase-ff6549a85b545638.yaml b/releasenotes/notes/gate-phase-ff6549a85b545638.yaml new file mode 100644 index 000000000000..71c42688a3ce --- /dev/null +++ b/releasenotes/notes/gate-phase-ff6549a85b545638.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + Adds `phase` attribute to the :class:`qiskit.circuit.Gate` class that can be + used set a custom phase coefficient to a gate. This will be reflected in + the :meth:`~qiskit.circuit.Gate.to_matrix` method which will return the + matrix :math:`e^{-i \theta} U` where :math:`U` is the canonical matrix + definition of the gate. + - | + Updates :class:`qiskit.circuit.library.RZGate` to have correct phase + when unrolled to a :class:`qiskit.circuit.library.U1Gate`. + - | + Updates :class:`qiskit.quantum_info.synthesis.OneQubitEulerDecomposer` to + perform phase correct synthesis of any single-qubit unitary matrix.