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 all 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
27 changes: 20 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,36 @@ 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`). In the case of several standard
gates, such as :class:`.RXGate`, when the power of a gate can be expressed in terms of another
standard gate that is returned directly.

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. In the case of several standard
gates, such as :class:`.RXGate`, this argument is ignored when
the power of a gate can be expressed in terms of another
standard gate.

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
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/i.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ def inverse(self, annotated: bool = False):
."""
return IGate() # self-inverse

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
return IGate()

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

self.definition = qc

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
return XXPlusYYGate(-np.pi * exponent)

def __eq__(self, other):
Expand Down
6 changes: 2 additions & 4 deletions qiskit/circuit/library/standard_gates/p.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +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):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
(theta,) = self.params
return PhaseGate(exponent * theta)

Expand Down Expand Up @@ -289,8 +288,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):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
(theta,) = self.params
return CPhaseGate(exponent * theta)

Expand Down
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/r.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +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):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
theta, phi = self.params
return RGate(exponent * theta, phi)

Expand Down
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/rx.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +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):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
(theta,) = self.params
return RXGate(exponent * theta)

Expand Down
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/rxx.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ def __array__(self, dtype=None):
dtype=dtype,
)

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
(theta,) = self.params
return RXXGate(exponent * theta)

Expand Down
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/ry.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +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):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
(theta,) = self.params
return RYGate(exponent * theta)

Expand Down
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/ryy.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ def __array__(self, dtype=None):
dtype=dtype,
)

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
(theta,) = self.params
return RYYGate(exponent * theta)

Expand Down
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/rz.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +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):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
(theta,) = self.params
return RZGate(exponent * theta)

Expand Down
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/rzx.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ def __array__(self, dtype=None):
dtype=dtype,
)

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
(theta,) = self.params
return RZXGate(exponent * theta)

Expand Down
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/rzz.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ def __array__(self, dtype=None):
dtype=dtype,
)

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
(theta,) = self.params
return RZZGate(exponent * theta)

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

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
from .p import PhaseGate

return PhaseGate(0.5 * numpy.pi * exponent)
Expand Down Expand Up @@ -172,8 +171,7 @@ def inverse(self, annotated: bool = False):
"""
return SGate()

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
from .p import PhaseGate

return PhaseGate(-0.5 * numpy.pi * exponent)
Expand Down Expand Up @@ -259,8 +257,7 @@ def inverse(self, annotated: bool = False):
"""
return CSdgGate(ctrl_state=self.ctrl_state)

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
from .p import CPhaseGate

return CPhaseGate(0.5 * numpy.pi * exponent)
Expand Down Expand Up @@ -345,8 +342,7 @@ def inverse(self, annotated: bool = False):
"""
return CSGate(ctrl_state=self.ctrl_state)

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
from .p import CPhaseGate

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

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
return PhaseGate(0.25 * numpy.pi * exponent)

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

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
return PhaseGate(-0.25 * numpy.pi * exponent)

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

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
theta, beta = self.params
return XXMinusYYGate(exponent * theta, beta)

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

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
theta, beta = self.params
return XXPlusYYGate(exponent * theta, beta)

Expand Down
3 changes: 1 addition & 2 deletions qiskit/circuit/library/standard_gates/z.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,7 @@ def inverse(self, annotated: bool = False):
"""
return ZGate() # self-inverse

def power(self, exponent: float):
"""Raise gate to a power."""
def power(self, exponent: float, annotated: bool = False):
return PhaseGate(numpy.pi * exponent)

def __eq__(self, other):
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
Loading
Loading