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

enable inverse of classically conditioned gate #6829

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
8 changes: 7 additions & 1 deletion qiskit/circuit/controlledgate.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""Controlled unitary gate."""

import copy
from typing import List, Optional, Union
from typing import List, Optional, Union, Tuple, TYPE_CHECKING

from qiskit.circuit.exceptions import CircuitError

Expand All @@ -23,6 +23,9 @@
from .quantumregister import QuantumRegister
from ._utils import _ctrl_state_to_int

# pylint: disable=cyclic-import
from .classicalregister import ClassicalRegister, Clbit


class ControlledGate(Gate):
"""Controlled unitary gate."""
Expand All @@ -37,6 +40,7 @@ def __init__(
definition: Optional["QuantumCircuit"] = None,
ctrl_state: Optional[Union[int, str]] = None,
base_gate: Optional[Gate] = None,
condition: Optional[Tuple[Union["ClassicalRegister", "Clbit"], int]] = None,
):
"""Create a new ControlledGate. In the new gate the first ``num_ctrl_qubits``
of the gate are the controls.
Expand All @@ -55,6 +59,8 @@ def __init__(
must equal num_ctrl_qubits, MSB on left. If None, use
2**num_ctrl_qubits-1.
base_gate: Gate object to be controlled.
condition (tuple[ClassicalRegister or Clbit, int] or None: classical
condition of instruction.

Raises:
CircuitError: If ``num_ctrl_qubits`` >= ``num_qubits``.
Expand Down
11 changes: 9 additions & 2 deletions qiskit/circuit/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ class Gate(Instruction):
"""Unitary gate."""

def __init__(
self, name: str, num_qubits: int, params: List, label: Optional[str] = None
self,
name: str,
num_qubits: int,
params: List,
label: Optional[str] = None,
condition: Optional[Tuple] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new docstring for "condition" needs to be here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

) -> None:
"""Create a new gate.

Expand All @@ -35,9 +40,11 @@ def __init__(
num_qubits: The number of qubits the gate acts on.
params: A list of parameters.
label: An optional label for the gate.
condition (tuple[ClassicalRegister or Clbit, int] or None: classical
condition of instruction.
"""
self.definition = None
super().__init__(name, num_qubits, 0, params, label=label)
super().__init__(name, num_qubits, 0, params, label=label, condition=condition)

# Set higher priority than Numpy array and matrix classes
__array_priority__ = 20
Expand Down
67 changes: 55 additions & 12 deletions qiskit/circuit/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,17 @@ class Instruction:
# NOTE: Using this attribute may change in the future (See issue # 5811)
_directive = False

def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit="dt", label=None):
def __init__(
self,
name,
num_qubits,
num_clbits,
params,
duration=None,
unit="dt",
label=None,
condition=None,
):
"""Create a new instruction.

Args:
Expand All @@ -65,6 +75,8 @@ def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit="dt
duration (int or float): instruction's duration. it must be integer if ``unit`` is 'dt'
unit (str): time unit of duration
label (str or None): An optional label for identifying the instruction.
condition (tuple[ClassicalRegister or Clbit, int] or None: classical
condition of instruction.

Raises:
CircuitError: when the register is not in the correct format.
Expand All @@ -88,7 +100,8 @@ def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit="dt
self._label = label
# tuple (ClassicalRegister, int), tuple (Clbit, bool) or tuple (Clbit, int)
# when the instruction has a conditional ("if")
self.condition = None
self._cl_condition = None # note: "_condition" was taken by the "assemble" method
self.condition = condition
# list of instructions (and their contexts) that this instruction is composed of
# empty definition means opaque or fundamental instruction
self._definition = None
Expand All @@ -108,13 +121,15 @@ def __eq__(self, other):
Returns:
bool: are self and other equal.
"""
if (
neq_cond = bool(
type(self) is not type(other)
or self.name != other.name
or self.num_qubits != other.num_qubits
or self.num_clbits != other.num_clbits
or self.definition != other.definition
):
or self.condition != other.condition
)
if neq_cond:
return False

for self_param, other_param in zip_longest(self.params, other.params):
Expand Down Expand Up @@ -311,6 +326,41 @@ def label(self, name: str):
else:
raise TypeError("label expects a string or None")

@property
def condition(self) -> tuple:
"""Get the classical condition of the instruction.

Returns:
tuple(ClassicalRegister or Clbit, int) or None: classical condition.
"""
return self._cl_condition

@condition.setter
def condition(self, setting):
"""Set classical condition of instruction.

Args:
setting (tuple(ClassicalRegister or Clbit, int)): where the first
element are the bits and the second is the value.
Raises:
CircuitError: type/value errors.
"""
if setting is None:
return
classical, value = setting
if not isinstance(classical, (ClassicalRegister, Clbit)):
breakpoint()
raise CircuitError(
"condition must be used with a classical register or " "classical bit"
)
if value < 0:
raise CircuitError("condition valueue should be non-negative")
if isinstance(classical, Clbit):
# Casting the conditional valueue as Boolean when
# the classical condition is on a classical bit.
value = bool(value)
self._cl_condition = (classical, value)

def mirror(self):
"""DEPRECATED: use instruction.reverse_ops().

Expand Down Expand Up @@ -376,6 +426,7 @@ def inverse(self):
num_qubits=self.num_qubits,
num_clbits=self.num_clbits,
params=self.params.copy(),
condition=self.condition,
)

else:
Expand All @@ -394,14 +445,6 @@ def inverse(self):

def c_if(self, classical, val):
"""Add classical condition on register or cbit classical and value val."""
if not isinstance(classical, (ClassicalRegister, Clbit)):
raise CircuitError("c_if must be used with a classical register or classical bit")
if val < 0:
raise CircuitError("condition value should be non-negative")
if isinstance(classical, Clbit):
# Casting the conditional value as Boolean when
# the classical condition is on a classical bit.
val = bool(val)
self.condition = (classical, val)
return self

Expand Down
4 changes: 2 additions & 2 deletions qiskit/circuit/library/generalized_gates/gms.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ class MSGate(Gate):
and is thus reduced to the RXXGate.
"""

def __init__(self, num_qubits, theta, label=None):
def __init__(self, num_qubits, theta, label=None, condition=None):
"""Create new MS gate."""
super().__init__("ms", num_qubits, [theta], label=label)
super().__init__("ms", num_qubits, [theta], label=label, condition=condition)

def _define(self):
theta = self.params[0]
Expand Down
6 changes: 3 additions & 3 deletions qiskit/circuit/library/generalized_gates/pauli.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ class PauliGate(Gate):
the pauli gates sequentially using standard Qiskit gates
"""

def __init__(self, label):
super().__init__("pauli", len(label), [label])
def __init__(self, label, condition=None):
super().__init__("pauli", len(label), [label], condition=condition)

def _define(self):
"""
Expand All @@ -55,7 +55,7 @@ def _define(self):

def inverse(self):
r"""Return inverted pauli gate (itself)."""
return PauliGate(self.params[0]) # self-inverse
return PauliGate(self.params[0], condition=self.condition) # self-inverse

def __array__(self, dtype=None):
"""Return a Numpy.array for the pauli gate.
Expand Down
6 changes: 3 additions & 3 deletions qiskit/circuit/library/generalized_gates/rv.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class RVGate(Gate):
\end{pmatrix}
"""

def __init__(self, v_x, v_y, v_z, basis="U"):
def __init__(self, v_x, v_y, v_z, basis="U", condition=None):
"""Create new rv single-qubit gate.

Args:
Expand All @@ -55,7 +55,7 @@ def __init__(self, v_x, v_y, v_z, basis="U"):
# pylint: disable=cyclic-import
from qiskit.quantum_info.synthesis.one_qubit_decompose import OneQubitEulerDecomposer

super().__init__("rv", 1, [v_x, v_y, v_z])
super().__init__("rv", 1, [v_x, v_y, v_z], condition=condition)
self._decomposer = OneQubitEulerDecomposer(basis=basis)

def _define(self):
Expand All @@ -69,7 +69,7 @@ def _define(self):
def inverse(self):
"""Invert this gate."""
vx, vy, vz = self.params
return RVGate(-vx, -vy, -vz)
return RVGate(-vx, -vy, -vz, condition=self.condition)

def to_matrix(self):
"""Return a numpy.array for the R(v) gate."""
Expand Down
4 changes: 2 additions & 2 deletions qiskit/circuit/library/standard_gates/dcx.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ class DCXGate(Gate):
\end{pmatrix}
"""

def __init__(self):
def __init__(self, condition=None):
"""Create new DCX gate."""
super().__init__("dcx", 2, [])
super().__init__("dcx", 2, [], condition=condition)

def _define(self):
"""
Expand Down
4 changes: 2 additions & 2 deletions qiskit/circuit/library/standard_gates/ecr.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ class ECRGate(Gate):
\end{pmatrix}
"""

def __init__(self):
def __init__(self, condition=None):
"""Create new ECR gate."""
super().__init__("ecr", 2, [])
super().__init__("ecr", 2, [], condition=condition)

def _define(self):
"""
Expand Down
19 changes: 13 additions & 6 deletions qiskit/circuit/library/standard_gates/h.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ class HGate(Gate):
\end{pmatrix}
"""

def __init__(self, label=None):
def __init__(self, label=None, condition=None):
"""Create new H gate."""
super().__init__("h", 1, [], label=label)
super().__init__("h", 1, [], label=label, condition=condition)

def _define(self):
"""
Expand Down Expand Up @@ -89,7 +89,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):

def inverse(self):
r"""Return inverted H gate (itself)."""
return HGate() # self-inverse
return HGate(condition=self.condition) # self-inverse

def __array__(self, dtype=None):
"""Return a Numpy.array for the H gate."""
Expand Down Expand Up @@ -158,10 +158,17 @@ class CHGate(ControlledGate):
dtype=complex,
)

def __init__(self, label=None, ctrl_state=None):
def __init__(self, label=None, ctrl_state=None, condition=None):
"""Create new CH gate."""
super().__init__(
"ch", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=HGate()
"ch",
2,
[],
num_ctrl_qubits=1,
label=label,
ctrl_state=ctrl_state,
base_gate=HGate(),
condition=condition,
)

def _define(self):
Expand Down Expand Up @@ -198,7 +205,7 @@ def _define(self):

def inverse(self):
"""Return inverted CH gate (itself)."""
return CHGate(ctrl_state=self.ctrl_state) # self-inverse
return CHGate(ctrl_state=self.ctrl_state, condition=self.condition) # self-inverse

def __array__(self, dtype=None):
"""Return a numpy.array for the CH gate."""
Expand Down
6 changes: 3 additions & 3 deletions qiskit/circuit/library/standard_gates/i.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ class IGate(Gate):
└───┘
"""

def __init__(self, label=None):
def __init__(self, label=None, condition=None):
"""Create new Identity gate."""
super().__init__("id", 1, [], label=label)
super().__init__("id", 1, [], label=label, condition=condition)

def inverse(self):
"""Invert this gate."""
return IGate() # self-inverse
return IGate(condition=self.condition) # self-inverse

def __array__(self, dtype=None):
"""Return a numpy.array for the identity gate."""
Expand Down
4 changes: 2 additions & 2 deletions qiskit/circuit/library/standard_gates/ms.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class MSGate(Gate):
and is thus reduced to the RXXGate.
"""

def __init__(self, num_qubits, theta, label=None):
def __init__(self, num_qubits, theta, label=None, condition=None):
"""Create new MS gate."""
warnings.warn(
"The qiskit.circuit.library.standard_gates.ms import "
Expand All @@ -40,7 +40,7 @@ def __init__(self, num_qubits, theta, label=None):
DeprecationWarning,
stacklevel=2,
)
super().__init__("ms", num_qubits, [theta], label=label)
super().__init__("ms", num_qubits, [theta], label=label, condition=condition)

def _define(self):
# pylint: disable=cyclic-import
Expand Down
Loading