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

Adding annotated argument to power methods #12101

Merged
merged 4 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions qiskit/circuit/annotated_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,24 @@ def inverse(self, annotated: bool = True):
extended_modifiers.append(InverseModifier())
return AnnotatedOperation(self.base_op, extended_modifiers)

def power(self, exponent: float, annotated: bool = False):
"""
Raise this gate to the power of ``exponent``.

Implemented as an annotated operation, see :class:`.AnnotatedOperation`.

Args:
exponent: the power to raise the gate to
annotated: ignored (used for consistency with other power methods)

Returns:
An operation implementing ``gate^exponent``
"""
# pylint: disable=unused-argument
extended_modifiers = self.modifiers.copy()
extended_modifiers.append(PowerModifier(exponent))
return AnnotatedOperation(self.base_op, extended_modifiers)


def _canonicalize_modifiers(modifiers):
"""
Expand Down
22 changes: 15 additions & 7 deletions qiskit/circuit/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from qiskit.circuit.parameterexpression import ParameterExpression
from qiskit.circuit.exceptions import CircuitError
from .annotated_operation import AnnotatedOperation, ControlModifier
from .annotated_operation import AnnotatedOperation, ControlModifier, PowerModifier
from .instruction import Instruction


Expand Down Expand Up @@ -62,23 +62,31 @@ def to_matrix(self) -> np.ndarray:
return self.__array__(dtype=complex)
raise CircuitError(f"to_matrix not defined for this {type(self)}")

def power(self, exponent: float):
"""Creates a unitary gate as `gate^exponent`.
def power(self, exponent: float, annotated: bool = False):
"""Raise this gate to the power of ``exponent``.

Implemented either as a unitary gate (ref. :class:`~.library.UnitaryGate`)
or as an annotated operation (ref. :class:`.AnnotatedOperation`).
mtreinish marked this conversation as resolved.
Show resolved Hide resolved

Args:
exponent (float): Gate^exponent
exponent (float): the power to raise the gate to
annotated (bool): indicates whether the power gate can be implemented
as an annotated operation.
mtreinish marked this conversation as resolved.
Show resolved Hide resolved

Returns:
.library.UnitaryGate: To which `to_matrix` is self.to_matrix^exponent.
An operation implementing ``gate^exponent``

Raises:
CircuitError: If Gate is not unitary
CircuitError: If gate is not unitary
"""
# pylint: disable=cyclic-import
from qiskit.quantum_info.operators import Operator
from qiskit.circuit.library.generalized_gates.unitary import UnitaryGate

return UnitaryGate(Operator(self).power(exponent), label=f"{self.name}^{exponent}")
if not annotated:
return UnitaryGate(Operator(self).power(exponent), label=f"{self.name}^{exponent}")
else:
return AnnotatedOperation(self, PowerModifier(exponent))

def __pow__(self, exponent: float) -> "Gate":
return self.power(exponent)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/i.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def inverse(self, annotated: bool = False):
."""
return IGate() # self-inverse

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
return IGate()

Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/iswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def _define(self):

self.definition = qc

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
return XXPlusYYGate(-np.pi * exponent)

Expand Down
4 changes: 2 additions & 2 deletions qiskit/circuit/library/standard_gates/p.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ 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):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
(theta,) = self.params
return PhaseGate(exponent * theta)
Expand Down Expand Up @@ -289,7 +289,7 @@ 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):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
(theta,) = self.params
return CPhaseGate(exponent * theta)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/r.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def __array__(self, dtype=None):
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):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
theta, phi = self.params
return RGate(exponent * theta, phi)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/rx.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ 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):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
(theta,) = self.params
return RXGate(exponent * theta)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/rxx.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def __array__(self, dtype=None):
dtype=dtype,
)

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
(theta,) = self.params
return RXXGate(exponent * theta)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/ry.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ 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):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
(theta,) = self.params
return RYGate(exponent * theta)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/ryy.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def __array__(self, dtype=None):
dtype=dtype,
)

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
(theta,) = self.params
return RYYGate(exponent * theta)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/rz.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ 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):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
(theta,) = self.params
return RZGate(exponent * theta)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/rzx.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def __array__(self, dtype=None):
dtype=dtype,
)

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
(theta,) = self.params
return RZXGate(exponent * theta)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/rzz.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def __array__(self, dtype=None):
dtype=dtype,
)

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
(theta,) = self.params
return RZZGate(exponent * theta)
Expand Down
8 changes: 4 additions & 4 deletions qiskit/circuit/library/standard_gates/s.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def inverse(self, annotated: bool = False):
"""
return SdgGate()

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
from .p import PhaseGate

Expand Down Expand Up @@ -172,7 +172,7 @@ def inverse(self, annotated: bool = False):
"""
return SGate()

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
from .p import PhaseGate

Expand Down Expand Up @@ -259,7 +259,7 @@ def inverse(self, annotated: bool = False):
"""
return CSdgGate(ctrl_state=self.ctrl_state)

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
from .p import CPhaseGate

Expand Down Expand Up @@ -345,7 +345,7 @@ def inverse(self, annotated: bool = False):
"""
return CSGate(ctrl_state=self.ctrl_state)

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
from .p import CPhaseGate

Expand Down
4 changes: 2 additions & 2 deletions qiskit/circuit/library/standard_gates/t.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def inverse(self, annotated: bool = False):
"""
return TdgGate()

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
return PhaseGate(0.25 * numpy.pi * exponent)

Expand Down Expand Up @@ -168,7 +168,7 @@ def inverse(self, annotated: bool = False):
"""
return TGate()

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
return PhaseGate(-0.25 * numpy.pi * exponent)

Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/xx_minus_yy.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def __array__(self, dtype=complex):
dtype=dtype,
)

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
theta, beta = self.params
return XXMinusYYGate(exponent * theta, beta)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/xx_plus_yy.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def __array__(self, dtype=complex):
dtype=dtype,
)

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
theta, beta = self.params
return XXPlusYYGate(exponent * theta, beta)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/library/standard_gates/z.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def inverse(self, annotated: bool = False):
"""
return ZGate() # self-inverse

def power(self, exponent: float):
def power(self, exponent: float, annotated: bool = False):
"""Raise gate to a power."""
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
return PhaseGate(numpy.pi * exponent)

Expand Down
36 changes: 24 additions & 12 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,26 +777,38 @@ def repeat(self, reps: int) -> "QuantumCircuit":

return repeated_circ

def power(self, power: float, matrix_power: bool = False) -> "QuantumCircuit":
def power(
self, power: float, matrix_power: bool = False, annotated: bool = False
) -> "QuantumCircuit":
"""Raise this circuit to the power of ``power``.

If ``power`` is a positive integer and ``matrix_power`` is ``False``, this implementation
defaults to calling ``repeat``. Otherwise, if the circuit is unitary, the matrix is
computed to calculate the matrix power.
If ``power`` is a positive integer and both ``matrix_power`` and ``annotated``
are ``False``, this implementation defaults to calling ``repeat``. Otherwise,
the circuit is converted into a gate, and a new circuit, containing this gate
raised to the given power, is returned. The gate raised to the given power is
implemented either as a unitary gate if ``annotated`` is ``False`` or as an
annotated operation if ``annotated`` is ``True``.

Args:
power (float): The power to raise this circuit to.
matrix_power (bool): If True, the circuit is converted to a matrix and then the
matrix power is computed. If False, and ``power`` is a positive integer,
the implementation defaults to ``repeat``.
matrix_power (bool): indicates whether the inner power gate can be implemented
as a unitary gate.
annotated (bool): indicates whether the inner power gate can be implemented
as an annotated operation.

Raises:
CircuitError: If the circuit needs to be converted to a gate but it is not unitary.
CircuitError: If the circuit needs to be converted to a unitary gate, but is
not unitary.

Returns:
QuantumCircuit: A circuit implementing this circuit raised to the power of ``power``.
"""
if power >= 0 and isinstance(power, (int, np.integer)) and not matrix_power:
if (
power >= 0
and isinstance(power, (int, np.integer))
and not matrix_power
and not annotated
):
return self.repeat(power)

# attempt conversion to gate
Expand All @@ -812,12 +824,12 @@ def power(self, power: float, matrix_power: bool = False) -> "QuantumCircuit":
except QiskitError as ex:
raise CircuitError(
"The circuit contains non-unitary operations and cannot be "
"controlled. Note that no qiskit.circuit.Instruction objects may "
"be in the circuit for this operation."
"raised to a power. Note that no qiskit.circuit.Instruction "
"objects may be in the circuit for this operation."
) from ex

power_circuit = QuantumCircuit(self.qubits, self.clbits, *self.qregs, *self.cregs)
power_circuit.append(gate.power(power), list(range(gate.num_qubits)))
power_circuit.append(gate.power(power, annotated=annotated), list(range(gate.num_qubits)))
return power_circuit

def control(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
features:
- |
The methods :meth:`~qiskit.circuit.QuantumCircuit.power`,
:meth:`~qiskit.circuit.Gate.power`, as well as the similar methods
of subclasses of :class:`~qiskit.circuit.Gate`
(such as of :class:`~qiskit.circuit.library.SGate`) all have an additional
argument ``annotated``.
The default value of ``False`` corresponds to the existing behavior.
Furthermore, for standard gates with an explicitly defined ``power`` method,
the argument ``annotated`` has no effect, for example both
``SGate().power(1.5, annotated=False)`` and ``SGate().power(1.5, annotated=True)``
return a ``PhaseGate``.
The difference manifests for gates without an explicitly defined
power method. The value of ``False`` returns a
:class:`~.library.UnitaryGate`, just as before, while the value of ``True``
returns an :class:`~.AnnotatedOperation` that represents the instruction
modified with the "power modifier".
2 changes: 1 addition & 1 deletion test/python/circuit/test_annotated_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from test import QiskitTestCase # pylint: disable=wrong-import-order


class TestAnnotatedOperationlass(QiskitTestCase):
class TestAnnotatedOperationClass(QiskitTestCase):
"""Testing qiskit.circuit.AnnotatedOperation"""

def test_create_gate_with_modifier(self):
Expand Down
17 changes: 16 additions & 1 deletion test/python/circuit/test_circuit_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from ddt import data, ddt

from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister
from qiskit.circuit import Gate, Instruction, Measure, Parameter, Barrier
from qiskit.circuit import Gate, Instruction, Measure, Parameter, Barrier, AnnotatedOperation
from qiskit.circuit.bit import Bit
from qiskit.circuit.classical import expr, types
from qiskit.circuit.classicalregister import Clbit
Expand Down Expand Up @@ -1013,6 +1013,21 @@ def test_power(self):
with self.subTest("negative power"):
self.assertEqual(qc.power(-2).data[0].operation, gate.power(-2))

with self.subTest("integer circuit power via annotation"):
power_qc = qc.power(4, annotated=True)
self.assertIsInstance(power_qc[0].operation, AnnotatedOperation)
self.assertEqual(Operator(power_qc), Operator(qc).power(4))

with self.subTest("float circuit power via annotation"):
power_qc = qc.power(1.5, annotated=True)
self.assertIsInstance(power_qc[0].operation, AnnotatedOperation)
self.assertEqual(Operator(power_qc), Operator(qc).power(1.5))

with self.subTest("negative circuit power via annotation"):
power_qc = qc.power(-2, annotated=True)
self.assertIsInstance(power_qc[0].operation, AnnotatedOperation)
self.assertEqual(Operator(power_qc), Operator(qc).power(-2))

def test_power_parameterized_circuit(self):
"""Test taking a parameterized circuit to a power."""
theta = Parameter("th")
Expand Down
Loading
Loading