diff --git a/qiskit/circuit/library/standard_gates/i.py b/qiskit/circuit/library/standard_gates/i.py index a30cfe7643fb..40e0976f69d7 100644 --- a/qiskit/circuit/library/standard_gates/i.py +++ b/qiskit/circuit/library/standard_gates/i.py @@ -55,3 +55,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the identity gate.""" return numpy.array([[1, 0], [0, 1]], dtype=dtype) + + def power(self, exponent: float): + """Raise gate to a power.""" + return IGate() diff --git a/qiskit/circuit/library/standard_gates/iswap.py b/qiskit/circuit/library/standard_gates/iswap.py index 1fe50d74e87a..dcc08c146047 100644 --- a/qiskit/circuit/library/standard_gates/iswap.py +++ b/qiskit/circuit/library/standard_gates/iswap.py @@ -13,10 +13,14 @@ """iSWAP gate.""" from typing import Optional + import numpy as np + from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from .xx_plus_yy import XXPlusYYGate + class iSwapGate(Gate): r"""iSWAP gate. @@ -96,6 +100,7 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit + from .h import HGate from .s import SGate from .x import CXGate @@ -118,3 +123,7 @@ def _define(self): def __array__(self, dtype=None): """Return a numpy.array for the iSWAP gate.""" return np.array([[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]], dtype=dtype) + + def power(self, exponent: float): + """Raise gate to a power.""" + return XXPlusYYGate(-np.pi * exponent) diff --git a/qiskit/circuit/library/standard_gates/p.py b/qiskit/circuit/library/standard_gates/p.py index 40ee94741a68..12a1528b8bd2 100644 --- a/qiskit/circuit/library/standard_gates/p.py +++ b/qiskit/circuit/library/standard_gates/p.py @@ -125,6 +125,11 @@ def __array__(self, dtype=None): lam = float(self.params[0]) return numpy.array([[1, 0], [0, exp(1j * lam)]], dtype=dtype) + def power(self, exponent: float): + """Raise gate to a power.""" + (theta,) = self.params + return PhaseGate(exponent * theta) + class CPhaseGate(ControlledGate): r"""Controlled-Phase gate. @@ -244,6 +249,11 @@ def __array__(self, dtype=None): ) return numpy.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, eith, 0], [0, 0, 0, 1]], dtype=dtype) + def power(self, exponent: float): + """Raise gate to a power.""" + (theta,) = self.params + return CPhaseGate(exponent * theta) + class MCPhaseGate(ControlledGate): r"""Multi-controlled-Phase gate. diff --git a/qiskit/circuit/library/standard_gates/r.py b/qiskit/circuit/library/standard_gates/r.py index 1452ae0b9bcb..292df90264d8 100644 --- a/qiskit/circuit/library/standard_gates/r.py +++ b/qiskit/circuit/library/standard_gates/r.py @@ -88,3 +88,8 @@ def __array__(self, dtype=None): exp_m = exp(-1j * phi) exp_p = exp(1j * phi) return numpy.array([[cos, -1j * exp_m * sin], [-1j * exp_p * sin, cos]], dtype=dtype) + + def power(self, exponent: float): + """Raise gate to a power.""" + theta, phi = self.params + return RGate(exponent * theta, phi) diff --git a/qiskit/circuit/library/standard_gates/rx.py b/qiskit/circuit/library/standard_gates/rx.py index 09139674886c..d26a9df60408 100644 --- a/qiskit/circuit/library/standard_gates/rx.py +++ b/qiskit/circuit/library/standard_gates/rx.py @@ -106,6 +106,11 @@ def __array__(self, dtype=None): sin = math.sin(self.params[0] / 2) return numpy.array([[cos, -1j * sin], [-1j * sin, cos]], dtype=dtype) + def power(self, exponent: float): + """Raise gate to a power.""" + (theta,) = self.params + return RXGate(exponent * theta) + class CRXGate(ControlledGate): r"""Controlled-RX gate. diff --git a/qiskit/circuit/library/standard_gates/rxx.py b/qiskit/circuit/library/standard_gates/rxx.py index e71e09ca9351..355f6bbcf215 100644 --- a/qiskit/circuit/library/standard_gates/rxx.py +++ b/qiskit/circuit/library/standard_gates/rxx.py @@ -119,3 +119,8 @@ def __array__(self, dtype=None): [[cos, 0, 0, -isin], [0, cos, -isin, 0], [0, -isin, cos, 0], [-isin, 0, 0, cos]], dtype=dtype, ) + + def power(self, exponent: float): + """Raise gate to a power.""" + (theta,) = self.params + return RXXGate(exponent * theta) diff --git a/qiskit/circuit/library/standard_gates/ry.py b/qiskit/circuit/library/standard_gates/ry.py index f96d75958eb7..a552cff5d087 100644 --- a/qiskit/circuit/library/standard_gates/ry.py +++ b/qiskit/circuit/library/standard_gates/ry.py @@ -105,6 +105,11 @@ def __array__(self, dtype=None): sin = math.sin(self.params[0] / 2) return numpy.array([[cos, -sin], [sin, cos]], dtype=dtype) + def power(self, exponent: float): + """Raise gate to a power.""" + (theta,) = self.params + return RYGate(exponent * theta) + class CRYGate(ControlledGate): r"""Controlled-RY gate. diff --git a/qiskit/circuit/library/standard_gates/ryy.py b/qiskit/circuit/library/standard_gates/ryy.py index 343e32947635..b15630aadceb 100644 --- a/qiskit/circuit/library/standard_gates/ryy.py +++ b/qiskit/circuit/library/standard_gates/ryy.py @@ -119,3 +119,8 @@ def __array__(self, dtype=None): [[cos, 0, 0, isin], [0, cos, -isin, 0], [0, -isin, cos, 0], [isin, 0, 0, cos]], dtype=dtype, ) + + def power(self, exponent: float): + """Raise gate to a power.""" + (theta,) = self.params + return RYYGate(exponent * theta) diff --git a/qiskit/circuit/library/standard_gates/rz.py b/qiskit/circuit/library/standard_gates/rz.py index 32fdc5be157c..f3da059f82ff 100644 --- a/qiskit/circuit/library/standard_gates/rz.py +++ b/qiskit/circuit/library/standard_gates/rz.py @@ -117,6 +117,11 @@ def __array__(self, dtype=None): ilam2 = 0.5j * float(self.params[0]) return np.array([[exp(-ilam2), 0], [0, exp(ilam2)]], dtype=dtype) + def power(self, exponent: float): + """Raise gate to a power.""" + (theta,) = self.params + return RZGate(exponent * theta) + class CRZGate(ControlledGate): r"""Controlled-RZ gate. diff --git a/qiskit/circuit/library/standard_gates/rzx.py b/qiskit/circuit/library/standard_gates/rzx.py index f1e25dc115b3..94de9513b5d2 100644 --- a/qiskit/circuit/library/standard_gates/rzx.py +++ b/qiskit/circuit/library/standard_gates/rzx.py @@ -165,3 +165,8 @@ def __array__(self, dtype=None): [[cos, 0, -isin, 0], [0, cos, 0, isin], [-isin, 0, cos, 0], [0, isin, 0, cos]], dtype=dtype, ) + + def power(self, exponent: float): + """Raise gate to a power.""" + (theta,) = self.params + return RZXGate(exponent * theta) diff --git a/qiskit/circuit/library/standard_gates/rzz.py b/qiskit/circuit/library/standard_gates/rzz.py index cd7bf27092e1..952e87c67765 100644 --- a/qiskit/circuit/library/standard_gates/rzz.py +++ b/qiskit/circuit/library/standard_gates/rzz.py @@ -132,3 +132,8 @@ def __array__(self, dtype=None): ], dtype=dtype, ) + + def power(self, exponent: float): + """Raise gate to a power.""" + (theta,) = self.params + return RZZGate(exponent * theta) diff --git a/qiskit/circuit/library/standard_gates/s.py b/qiskit/circuit/library/standard_gates/s.py index 5bda08fa2a69..eaef481d5961 100644 --- a/qiskit/circuit/library/standard_gates/s.py +++ b/qiskit/circuit/library/standard_gates/s.py @@ -13,11 +13,14 @@ """The S, Sdg, CS and CSdg gates.""" from typing import Optional, Union + import numpy -from qiskit.qasm import pi + from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate +from qiskit.circuit.library.standard_gates.p import CPhaseGate, PhaseGate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.qasm import pi class SGate(Gate): @@ -60,6 +63,7 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit + from .u1 import U1Gate q = QuantumRegister(1, "q") @@ -78,6 +82,10 @@ def __array__(self, dtype=None): """Return a numpy.array for the S gate.""" return numpy.array([[1, 0], [0, 1j]], dtype=dtype) + def power(self, exponent: float): + """Raise gate to a power.""" + return PhaseGate(0.5 * numpy.pi * exponent) + class SdgGate(Gate): r"""Single qubit S-adjoint gate (~Z**0.5). @@ -119,6 +127,7 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit + from .u1 import U1Gate q = QuantumRegister(1, "q") @@ -137,6 +146,10 @@ def __array__(self, dtype=None): """Return a numpy.array for the Sdg gate.""" return numpy.array([[1, 0], [0, -1j]], dtype=dtype) + def power(self, exponent: float): + """Raise gate to a power.""" + return PhaseGate(-0.5 * numpy.pi * exponent) + class CSGate(ControlledGate): r"""Controlled-S gate. @@ -194,9 +207,6 @@ def _define(self): """ gate cs a,b { h b; cp(pi/2) a,b; h b; } """ - # pylint: disable=cyclic-import - from .p import CPhaseGate - self.definition = CPhaseGate(theta=pi / 2).definition def inverse(self): @@ -210,6 +220,10 @@ def __array__(self, dtype=None): return numpy.asarray(mat, dtype=dtype) return mat + def power(self, exponent: float): + """Raise gate to a power.""" + return CPhaseGate(0.5 * numpy.pi * exponent) + class CSdgGate(ControlledGate): r"""Controlled-S^\dagger gate. @@ -273,9 +287,6 @@ def _define(self): """ gate csdg a,b { h b; cp(-pi/2) a,b; h b; } """ - # pylint: disable=cyclic-import - from .p import CPhaseGate - self.definition = CPhaseGate(theta=-pi / 2).definition def inverse(self): @@ -288,3 +299,7 @@ def __array__(self, dtype=None): if dtype is not None: return numpy.asarray(mat, dtype=dtype) return mat + + def power(self, exponent: float): + """Raise gate to a power.""" + return CPhaseGate(-0.5 * numpy.pi * exponent) diff --git a/qiskit/circuit/library/standard_gates/t.py b/qiskit/circuit/library/standard_gates/t.py index 9fddfa008e4c..5ca423e54220 100644 --- a/qiskit/circuit/library/standard_gates/t.py +++ b/qiskit/circuit/library/standard_gates/t.py @@ -13,10 +13,13 @@ """T and Tdg gate.""" import math from typing import Optional + import numpy -from qiskit.qasm import pi + from qiskit.circuit.gate import Gate +from qiskit.circuit.library.standard_gates.p import PhaseGate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.qasm import pi class TGate(Gate): @@ -60,6 +63,7 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit + from .u1 import U1Gate q = QuantumRegister(1, "q") @@ -78,6 +82,10 @@ def __array__(self, dtype=None): """Return a numpy.array for the T gate.""" return numpy.array([[1, 0], [0, (1 + 1j) / numpy.sqrt(2)]], dtype=dtype) + def power(self, exponent: float): + """Raise gate to a power.""" + return PhaseGate(0.25 * numpy.pi * exponent) + class TdgGate(Gate): r"""Single qubit T-adjoint gate (~Z**0.25). @@ -119,6 +127,7 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit + from .u1 import U1Gate q = QuantumRegister(1, "q") @@ -136,3 +145,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the inverse T gate.""" return numpy.array([[1, 0], [0, (1 - 1j) / math.sqrt(2)]], dtype=dtype) + + def power(self, exponent: float): + """Raise gate to a power.""" + return PhaseGate(-0.25 * numpy.pi * exponent) diff --git a/qiskit/circuit/library/standard_gates/xx_minus_yy.py b/qiskit/circuit/library/standard_gates/xx_minus_yy.py index 1298c79d29fe..df7fb1f8504f 100644 --- a/qiskit/circuit/library/standard_gates/xx_minus_yy.py +++ b/qiskit/circuit/library/standard_gates/xx_minus_yy.py @@ -169,3 +169,8 @@ def __array__(self, dtype=complex): ], dtype=dtype, ) + + def power(self, exponent: float): + """Raise gate to a power.""" + theta, beta = self.params + return XXMinusYYGate(exponent * theta, beta) diff --git a/qiskit/circuit/library/standard_gates/xx_plus_yy.py b/qiskit/circuit/library/standard_gates/xx_plus_yy.py index 2d00003652ea..85d63ee625f9 100644 --- a/qiskit/circuit/library/standard_gates/xx_plus_yy.py +++ b/qiskit/circuit/library/standard_gates/xx_plus_yy.py @@ -170,3 +170,8 @@ def __array__(self, dtype=complex): ], dtype=dtype, ) + + def power(self, exponent: float): + """Raise gate to a power.""" + theta, beta = self.params + return XXPlusYYGate(exponent * theta, beta) diff --git a/qiskit/circuit/library/standard_gates/z.py b/qiskit/circuit/library/standard_gates/z.py index 9fad215878f9..fcf7334df5eb 100644 --- a/qiskit/circuit/library/standard_gates/z.py +++ b/qiskit/circuit/library/standard_gates/z.py @@ -13,12 +13,16 @@ """Z, CZ and CCZ gates.""" from typing import Optional, Union + import numpy -from qiskit.qasm import pi + +from qiskit.circuit._utils import _compute_control_matrix from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit._utils import _compute_control_matrix +from qiskit.qasm import pi + +from .p import PhaseGate class ZGate(Gate): @@ -74,6 +78,7 @@ def __init__(self, label: Optional[str] = None): def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit + from .u1 import U1Gate q = QuantumRegister(1, "q") @@ -117,6 +122,10 @@ def __array__(self, dtype=None): """Return a numpy.array for the Z gate.""" return numpy.array([[1, 0], [0, -1]], dtype=dtype) + def power(self, exponent: float): + """Raise gate to a power.""" + return PhaseGate(numpy.pi * exponent) + class CZGate(ControlledGate): r"""Controlled-Z gate. @@ -163,6 +172,7 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit + from .h import HGate from .x import CXGate @@ -241,6 +251,7 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit + from .h import HGate from .x import CCXGate diff --git a/releasenotes/notes/efficient-gate-power-effa21e3ee4581ee.yaml b/releasenotes/notes/efficient-gate-power-effa21e3ee4581ee.yaml new file mode 100644 index 000000000000..46b00ebd68f0 --- /dev/null +++ b/releasenotes/notes/efficient-gate-power-effa21e3ee4581ee.yaml @@ -0,0 +1,25 @@ +--- +features: + - | + Overrides some gate power methods to make them more efficient and less lossy: + - Powering a :class:`~.CPhaseGate` gives a :class:`~.CPhaseGate`. + - Powering a :class:`~.CSGate` gives a :class:`~.CPhaseGate`. + - Powering a :class:`~.CSdgGate` gives a :class:`~.CPhaseGate`. + - Powering an :class:`~.IGate` gives an :class:`~.IGate`. + - Powering a :class:`~.PhaseGate` gives a :class:`~.PhaseGate`. + - Powering an :class:`~.RGate` gives an :class:`~.RGate`. + - Powering an :class:`~.RXGate` gives an :class:`~.RXGate`. + - Powering an :class:`~.RXXGate` gives an :class:`~.RXXGate`. + - Powering an :class:`~.RYGate` gives an :class:`~.RYGate`. + - Powering an :class:`~.RYYGate` gives an :class:`~.RYYGate`. + - Powering an :class:`~.RZGate` gives an :class:`~.RZGate`. + - Powering an :class:`~.RZXGate` gives an :class:`~.RZXGate`. + - Powering an :class:`~.RZZGate` gives an :class:`~.RZZGate`. + - Powering a :class:`~.SdgGate` gives a :class:`~.PhaseGate`. + - Powering an :class:`~.SGate` gives a :class:`~.PhaseGate`. + - Powering a :class:`~.TdgGate` gives a :class:`~.PhaseGate`. + - Powering a :class:`~.TGate` gives a :class:`~.PhaseGate`. + - Powering an :class:`~.XXMinusYYGate` gives an :class:`~.XXMinusYYGate`. + - Powering an :class:`~.XXPlusYYGate` gives an :class:`~.XXPlusYYGate`. + - Powering a :class:`~.ZGate` gives a :class:`~.PhaseGate`. + - Powering an :class:`~.iSwapGate` gives an :class:`~.XXPlusYYGate`. \ No newline at end of file diff --git a/test/python/circuit/test_gate_power.py b/test/python/circuit/test_gate_power.py index 3ea821dd5546..43de932ed246 100644 --- a/test/python/circuit/test_gate_power.py +++ b/test/python/circuit/test_gate_power.py @@ -14,13 +14,40 @@ """Test Qiskit's power instruction operation.""" import unittest -from ddt import ddt, data -from numpy import array, eye +from typing import Type + +import numpy as np +import scipy.linalg +from ddt import data, ddt, unpack -from qiskit.test import QiskitTestCase -from qiskit.extensions import SGate, UnitaryGate, CXGate from qiskit.circuit import Gate, QuantumCircuit +from qiskit.extensions import ( + CPhaseGate, + CSdgGate, + CSGate, + CXGate, + IGate, + PhaseGate, + RGate, + RXGate, + RXXGate, + RYGate, + RYYGate, + RZGate, + RZXGate, + RZZGate, + SdgGate, + SGate, + TdgGate, + TGate, + UnitaryGate, + XXMinusYYGate, + XXPlusYYGate, + ZGate, + iSwapGate, +) from qiskit.quantum_info.operators import Operator +from qiskit.test import QiskitTestCase @ddt @@ -31,16 +58,14 @@ class TestPowerSgate(QiskitTestCase): def test_sgate_int(self, n): """Test Sgate.power(n) method with n as integer.""" result = SGate().power(n) - - self.assertEqual(result.label, "s^%s" % n) - self.assertIsInstance(result, UnitaryGate) + self.assertIsInstance(result, PhaseGate) self.assertEqual(Operator(result), Operator(SGate()).power(n)) results = { - 1.5: array([[1, 0], [0, -0.70710678 + 0.70710678j]], dtype=complex), - 0.1: array([[1, 0], [0, 0.98768834 + 0.15643447j]], dtype=complex), - -1.5: array([[1, 0], [0, -0.70710678 - 0.70710678j]], dtype=complex), - -0.1: array([[1, 0], [0, 0.98768834 - 0.15643447j]], dtype=complex), + 1.5: np.array([[1, 0], [0, -0.70710678 + 0.70710678j]], dtype=complex), + 0.1: np.array([[1, 0], [0, 0.98768834 + 0.15643447j]], dtype=complex), + -1.5: np.array([[1, 0], [0, -0.70710678 - 0.70710678j]], dtype=complex), + -0.1: np.array([[1, 0], [0, 0.98768834 - 0.15643447j]], dtype=complex), } @data(1.5, 0.1, -1.5, -0.1) @@ -49,8 +74,7 @@ def test_sgate_float(self, n): result = SGate().power(n) expected = self.results[n] - self.assertEqual(result.label, "s^%s" % n) - self.assertIsInstance(result, UnitaryGate) + self.assertIsInstance(result, PhaseGate) self.assertEqual(Operator(result), Operator(expected)) @@ -59,11 +83,11 @@ class TestPowerIntCX(QiskitTestCase): """Test CX.power() with integer""" results = { - 2: array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], dtype=complex), - 1: array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), - 0: array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], dtype=complex), - -1: array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), - -2: array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], dtype=complex), + 2: np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], dtype=complex), + 1: np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), + 0: np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], dtype=complex), + -1: np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), + -2: np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], dtype=complex), } @data(2, 1, 0, -2) @@ -81,7 +105,7 @@ class TestGateSqrt(QiskitTestCase): def test_unitary_sqrt(self): """Test UnitaryGate.power(1/2) method.""" - expected = array([[0.5 + 0.5j, 0.5 + 0.5j], [-0.5 - 0.5j, 0.5 + 0.5j]], dtype=complex) + expected = np.array([[0.5 + 0.5j, 0.5 + 0.5j], [-0.5 - 0.5j, 0.5 + 0.5j]], dtype=complex) result = UnitaryGate([[0, 1j], [-1j, 0]]).power(1 / 2) @@ -92,11 +116,10 @@ def test_unitary_sqrt(self): def test_standard_sqrt(self): """Test standard Gate.power(1/2) method.""" - expected = array([[1, 0], [0, 0.70710678118 + 0.70710678118j]], dtype=complex) + expected = np.array([[1, 0], [0, 0.70710678118 + 0.70710678118j]], dtype=complex) result = SGate().power(1 / 2) - self.assertEqual(result.label, "s^0.5") self.assertEqual(len(result.definition), 1) self.assertIsInstance(result, Gate) self.assertEqual(Operator(result), Operator(expected)) @@ -104,7 +127,6 @@ def test_standard_sqrt(self): def test_composite_sqrt(self): """Test composite Gate.power(1/2) method.""" circ = QuantumCircuit(1, name="my_gate") - import numpy as np thetaz = 0.1 thetax = 0.2 @@ -141,7 +163,6 @@ def test_direct_root(self, degree): """Test nth root""" result = SGate().power(1 / degree) - self.assertEqual(result.label, "s^" + str(1 / degree)) self.assertEqual(len(result.definition), 1) self.assertIsInstance(result, Gate) self.assertEqual(Operator(result).power(degree), Operator(SGate())) @@ -151,7 +172,6 @@ def test_float_gt_one(self, exponent): """Test greater-than-one exponents""" result = SGate().power(exponent) - self.assertEqual(result.label, "s^" + str(exponent)) self.assertEqual(len(result.definition), 1) self.assertIsInstance(result, Gate) # SGate().to_matrix() is diagonal so `**` is equivalent. @@ -161,11 +181,10 @@ def test_minus_zero_two(self, exponent=-0.2): """Test Sgate^(-0.2)""" result = SGate().power(exponent) - self.assertEqual(result.label, "s^" + str(exponent)) self.assertEqual(len(result.definition), 1) self.assertIsInstance(result, Gate) self.assertEqual( - Operator(array([[1, 0], [0, 0.95105652 - 0.30901699j]], dtype=complex)), + Operator(np.array([[1, 0], [0, 0.95105652 - 0.30901699j]], dtype=complex)), Operator(result), ) @@ -178,7 +197,6 @@ class TestPowerInvariant(QiskitTestCase): def test_invariant1_int(self, n): """Test (op^(1/n))^(n) == op, integer n""" result = SGate().power(1 / n).power(n) - self.assertEqual(result.label, "unitary^" + str(n)) self.assertEqual(len(result.definition), 1) self.assertIsInstance(result, Gate) self.assertTrue(Operator(SGate()), Operator(result)) @@ -187,11 +205,49 @@ def test_invariant1_int(self, n): def test_invariant2(self, n): """Test op^(n) * op^(-n) == I""" result = Operator(SGate()).power(n) & Operator(SGate()).power(-n) - expected = Operator(eye(2)) + expected = Operator(np.eye(2)) self.assertEqual(len(result.data), len(expected.data)) self.assertEqual(result, expected) +@ddt +class TestEfficientGatePowering(QiskitTestCase): + """Test gate powering is efficient where expected.""" + + @data( + (CPhaseGate(0.1), CPhaseGate), + (CSdgGate(), CPhaseGate), + (CSGate(), CPhaseGate), + (IGate(), IGate), + (PhaseGate(-0.1), PhaseGate), + (RGate(0.1, 0.1), RGate), + (RXGate(0.1), RXGate), + (RXXGate(-0.1), RXXGate), + (RYGate(-0.1), RYGate), + (RYYGate(0.1), RYYGate), + (RZGate(0.1), RZGate), + (RZXGate(-0.1), RZXGate), + (RZZGate(-0.1), RZZGate), + (SdgGate(), PhaseGate), + (SGate(), PhaseGate), + (TGate(), PhaseGate), + (TdgGate(), PhaseGate), + (XXMinusYYGate(-0.1, 0.1), XXMinusYYGate), + (XXPlusYYGate(2.1, 0.1), XXPlusYYGate), + (ZGate(), PhaseGate), + (iSwapGate(), XXPlusYYGate), + ) + @unpack + def test_efficient_gate_powering(self, gate: Gate, output_gate_type: Type[Gate]): + """Test efficient gate powering.""" + exponents = (-5, -0.5, -0.1, 0, 0.1, 0.5, 5) + for exponent in exponents: + result = gate.power(exponent) + self.assertIsInstance(result, output_gate_type) + expected = scipy.linalg.fractional_matrix_power(np.array(gate), exponent) + np.testing.assert_allclose(np.array(result), expected, atol=1e-8) + + if __name__ == "__main__": unittest.main()