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

Deprecate the condition attribute and related functionality #13223

Merged
merged 13 commits into from
Oct 31, 2024
Merged
2 changes: 2 additions & 0 deletions qiskit/circuit/barrier.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from __future__ import annotations

from qiskit.exceptions import QiskitError
from qiskit.utils import deprecate_func
from .instruction import Instruction


Expand All @@ -44,5 +45,6 @@ def inverse(self, annotated: bool = False):
"""Special case. Return self."""
return Barrier(self.num_qubits)

@deprecate_func(since="1.3.0", removal_timeline="in 2.0.0")
def c_if(self, classical, val):
raise QiskitError("Barriers are compiler directives and cannot be conditional.")
2 changes: 2 additions & 0 deletions qiskit/circuit/delay.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from qiskit.circuit.gate import Gate
from qiskit.circuit import _utils
from qiskit.circuit.parameterexpression import ParameterExpression
from qiskit.utils import deprecate_func


@_utils.with_gate_array(np.eye(2, dtype=complex))
Expand All @@ -42,6 +43,7 @@ def inverse(self, annotated: bool = False):
"""Special case. Return self."""
return self

@deprecate_func(since="1.3.0", removal_timeline="in 2.0.0")
def c_if(self, classical, val):
raise CircuitError("Conditional Delay is not yet implemented.")

Expand Down
2 changes: 2 additions & 0 deletions qiskit/circuit/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def to_mutable(self):
return self.copy()

@property
@deprecate_func(since="1.3.0", removal_timeline="in 2.0.0", is_property=True, stacklevel=2)
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
def condition(self):
"""The classical condition on the instruction."""
return self._condition
Expand Down Expand Up @@ -499,6 +500,7 @@ def inverse(self, annotated: bool = False):
inverse_gate.definition = inverse_definition
return inverse_gate

@deprecate_func(since="1.3.0", removal_timeline="in 2.0.0")
def c_if(self, classical, val):
"""Set a classical equality condition on this instruction between the register or cbit
``classical`` and value ``val``.
Expand Down
2 changes: 2 additions & 0 deletions qiskit/circuit/instructionset.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from typing import Callable

from qiskit.circuit.exceptions import CircuitError
from qiskit.utils import deprecate_func
from .classicalregister import Clbit, ClassicalRegister
from .operation import Operation
from .quantumcircuitdata import CircuitInstruction
Expand Down Expand Up @@ -105,6 +106,7 @@ def inverse(self, annotated: bool = False):
)
return self

@deprecate_func(since="1.3.0", removal_timeline="in 2.0.0")
def c_if(self, classical: Clbit | ClassicalRegister | int, val: int) -> "InstructionSet":
"""Set a classical equality condition on all the instructions in this set between the
:obj:`.ClassicalRegister` or :obj:`.Clbit` ``classical`` and value ``val``.
Expand Down
2 changes: 2 additions & 0 deletions qiskit/circuit/singleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ class XGate(Gate, metaclass=_SingletonMeta, overrides=_SingletonGateOverrides):

import functools

from qiskit.utils import deprecate_func
from .instruction import Instruction
from .gate import Gate
from .controlledgate import ControlledGate, _ctrl_state_to_int
Expand Down Expand Up @@ -489,6 +490,7 @@ class they are providing overrides for has more lazy attributes or user-exposed
instruction._params = _frozenlist(instruction._params)
return instruction

@deprecate_func(since="1.3.0", removal_timeline="in 2.0.0")
def c_if(self, classical, val):
return self.to_mutable().c_if(classical, val)

Expand Down
2 changes: 2 additions & 0 deletions qiskit/circuit/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import typing

from qiskit.utils import deprecate_func
from .exceptions import CircuitError
from .classical import expr, types
from .instruction import Instruction
Expand Down Expand Up @@ -88,6 +89,7 @@ def rvalue(self):
"""Get the r-value :class:`~.expr.Expr` node that is being written into the l-value."""
return self.params[1]

@deprecate_func(since="1.3.0", removal_timeline="in 2.0.0")
def c_if(self, classical, val):
""":meta hidden:"""
raise NotImplementedError(
Expand Down
17 changes: 16 additions & 1 deletion qiskit/qasm2/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# that they have been altered from the originals.

"""Python-space bytecode interpreter for the output of the main Rust parser logic."""

import warnings
import dataclasses
import math
from typing import Iterable, Callable
Expand Down Expand Up @@ -255,19 +255,34 @@ def from_bytecode(bytecode, custom_instructions: Iterable[CustomInstruction]):
CircuitInstruction(gates[gate_id](*parameters), [qubits[q] for q in op_qubits])
)
elif opcode == OpCode.ConditionedGate:
warnings.warn(
"Conditioned gates in qasm2 will be loaded as an IfElseOp starting in Qiskit 2.0",
FutureWarning,
stacklevel=3,
)
gate_id, parameters, op_qubits, creg, value = op.operands
gate = gates[gate_id](*parameters).c_if(qc.cregs[creg], value)
qc._append(CircuitInstruction(gate, [qubits[q] for q in op_qubits]))
elif opcode == OpCode.Measure:
qubit, clbit = op.operands
qc._append(CircuitInstruction(Measure(), (qubits[qubit],), (clbits[clbit],)))
elif opcode == OpCode.ConditionedMeasure:
warnings.warn(
"Conditioned measurements in qasm2 will be loaded as an IfElseOp starting in Qiskit 2.0",
FutureWarning,
stacklevel=3,
)
qubit, clbit, creg, value = op.operands
measure = Measure().c_if(qc.cregs[creg], value)
qc._append(CircuitInstruction(measure, (qubits[qubit],), (clbits[clbit],)))
elif opcode == OpCode.Reset:
qc._append(CircuitInstruction(Reset(), (qubits[op.operands[0]],)))
elif opcode == OpCode.ConditionedReset:
warnings.warn(
"Conditioned resets in qasm2 will be loaded as an IfElseOp starting in Qiskit 2.0",
FutureWarning,
stacklevel=3,
)
qubit, creg, value = op.operands
reset = Reset().c_if(qc.cregs[creg], value)
qc._append(CircuitInstruction(reset, (qubits[qubit],)))
Expand Down
13 changes: 13 additions & 0 deletions qiskit/qpy/binary_io/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,13 @@ def _read_instruction(
use_symengine,
standalone_vars,
)
if condition is not None:
warnings.warn(
f"The .condition attribute on {gate_name} will be loaded as an IfElseOp "
"starting in Qiskit 2.0",
FutureWarning,
stacklevel=3,
)
inst_obj.condition = condition
if instruction.label_size > 0:
inst_obj.label = label
Expand Down Expand Up @@ -414,6 +421,12 @@ def _read_instruction(
gate = gate_class(*params)
if condition:
if not isinstance(gate, ControlFlowOp):
warnings.warn(
f"The .condition attribute on {gate} will be loaded as an "
"IfElseOp starting in Qiskit 2.0",
FutureWarning,
stacklevel=3,
)
gate = gate.c_if(*condition)
else:
gate.condition = condition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
)
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.utils import deprecate_func


class ConvertConditionsToIfOps(TransformationPass):
Expand All @@ -31,6 +32,10 @@ class ConvertConditionsToIfOps(TransformationPass):
This is a simple pass aimed at easing the conversion from the old style of using
:meth:`.InstructionSet.c_if` into the new style of using more complex conditional logic."""

@deprecate_func(since="1.3.0", removal_timeline="in Qiskit 2.0.0")
def __init__(self):
super().__init__()

def _run_inner(self, dag):
"""Run the pass on one :class:`.DAGCircuit`, mutating it. Returns ``True`` if the circuit
was modified and ``False`` if not."""
Expand Down
38 changes: 38 additions & 0 deletions releasenotes/notes/deprecate-condition_c_if-9548e5522814fe9c.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
deprecations_circuits:
- |
Deprecated the :attr:`.Instruction.condition` and the
:meth:`.Instruction.c_if` method. This attribute and method will be removed
in Qiskit 2.0 and along with any other uses of this in the Qiskit data
model. This functionality has been superseded by the :class:`.IfElseOp`
which can be used to describe a classically condition in a circuit. For
example, a circuit using :attr:`.Instruction.condition` like::
mtreinish marked this conversation as resolved.
Show resolved Hide resolved

from qiskit.circuit import QuantumCircuit

qc = QuantumCircuit(2, 2)
qc.h(0)
qc.x(0).c_if(0, 1)
qc.z(1.c_if(1, 0)
qc.measure(0, 0)
qc.measure(1, 1)

can be rewritten using as::
mtreinish marked this conversation as resolved.
Show resolved Hide resolved

qc = QuantumCircuit(2, 2)
qc.h(0)
with expected.if_test((expected.clbits[0], True)):
qc.x(0)
with expected.if_test((expected.clbits[1], False)):
qc.z(1)
qc.measure(0, 0)
qc.measure(1, 1)

The now deprecated :class:`.ConvertConditionsToIfOps` transpiler pass can
be used to automate this conversion for existing circuits.
deprecations_transpiler:
- |
The transpiler pass :class:`.ConvertConditionsToIfOps` has been deprecated
and will be removed in Qiskit 2.0.0. This class is now deprecated because
the underlying data model for :attr:`.Instruction.condition` which this
pass is converting from has been deprecated and will be removed in 2.0.0.
12 changes: 8 additions & 4 deletions test/python/circuit/classical/test_expr_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ def test_lift_legacy_condition(self):
clbit = Clbit()

inst = Instruction("custom", 1, 0, [])
inst.c_if(cr, 7)
with self.assertWarns(DeprecationWarning):
inst.c_if(cr, 7)
self.assertEqual(
expr.lift_legacy_condition(inst.condition),
expr.Binary(
Expand All @@ -38,7 +39,8 @@ def test_lift_legacy_condition(self):
)

inst = Instruction("custom", 1, 0, [])
inst.c_if(cr, 255)
with self.assertWarns(DeprecationWarning):
inst.c_if(cr, 255)
self.assertEqual(
expr.lift_legacy_condition(inst.condition),
expr.Binary(
Expand All @@ -50,7 +52,8 @@ def test_lift_legacy_condition(self):
)

inst = Instruction("custom", 1, 0, [])
inst.c_if(clbit, False)
with self.assertWarns(DeprecationWarning):
inst.c_if(clbit, False)
self.assertEqual(
expr.lift_legacy_condition(inst.condition),
expr.Unary(
Expand All @@ -61,7 +64,8 @@ def test_lift_legacy_condition(self):
)

inst = Instruction("custom", 1, 0, [])
inst.c_if(clbit, True)
with self.assertWarns(DeprecationWarning):
inst.c_if(clbit, True)
self.assertEqual(
expr.lift_legacy_condition(inst.condition),
expr.Var(clbit, types.Bool()),
Expand Down
3 changes: 2 additions & 1 deletion test/python/circuit/library/test_permutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ def test_reverse_ops(self):
def test_conditional(self):
"""Test adding conditional permutations."""
qc = QuantumCircuit(5, 1)
qc.append(PermutationGate([1, 2, 0]), [2, 3, 4]).c_if(0, 1)
with self.assertWarns(DeprecationWarning):
qc.append(PermutationGate([1, 2, 0]), [2, 3, 4]).c_if(0, 1)
self.assertIsNotNone(qc.data[0].operation.condition)

def test_qasm(self):
Expand Down
3 changes: 2 additions & 1 deletion test/python/circuit/library/test_qft.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ def test_reverse_ops(self):
def test_conditional(self):
"""Test adding conditional to a QFTGate."""
qc = QuantumCircuit(5, 1)
qc.append(QFTGate(4), [1, 2, 0, 4]).c_if(0, 1)
with self.assertWarns(DeprecationWarning):
qc.append(QFTGate(4), [1, 2, 0, 4]).c_if(0, 1)
self.assertIsNotNone(qc.data[0].operation.condition)

def test_qasm(self):
Expand Down
40 changes: 26 additions & 14 deletions test/python/circuit/test_circuit_load_from_qpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,13 @@ def test_qpy_full_path(self):
def test_circuit_with_conditional(self):
"""Test that instructions with conditions are correctly serialized."""
qc = QuantumCircuit(1, 1)
qc.x(0).c_if(qc.cregs[0], 1)
with self.assertWarns(DeprecationWarning):
qc.x(0).c_if(qc.cregs[0], 1)
qpy_file = io.BytesIO()
dump(qc, qpy_file)
qpy_file.seek(0)
new_circ = load(qpy_file)[0]
with self.assertWarns(DeprecationWarning):
new_circ = load(qpy_file)[0]
self.assertEqual(qc, new_circ)
self.assertDeprecatedBitProperties(qc, new_circ)

Expand Down Expand Up @@ -408,13 +410,15 @@ def test_multiple_circuits(self):
"""Test multiple circuits can be serialized together."""
circuits = []
for i in range(10):
circuits.append(
random_circuit(10, 10, measure=True, conditional=True, reset=True, seed=42 + i)
)
with self.assertWarns(DeprecationWarning):
circuits.append(
random_circuit(10, 10, measure=True, conditional=True, reset=True, seed=42 + i)
)
qpy_file = io.BytesIO()
dump(circuits, qpy_file)
qpy_file.seek(0)
new_circs = load(qpy_file)
with self.assertWarns(DeprecationWarning):
new_circs = load(qpy_file)
self.assertEqual(circuits, new_circs)
for old, new in zip(circuits, new_circs):
self.assertDeprecatedBitProperties(old, new)
Expand Down Expand Up @@ -674,12 +678,14 @@ def test_circuit_with_conditional_with_label(self):
"""Test that instructions with conditions are correctly serialized."""
qc = QuantumCircuit(1, 1)
gate = XGate(label="My conditional x gate")
gate.c_if(qc.cregs[0], 1)
with self.assertWarns(DeprecationWarning):
gate.c_if(qc.cregs[0], 1)
qc.append(gate, [0])
qpy_file = io.BytesIO()
dump(qc, qpy_file)
qpy_file.seek(0)
new_circ = load(qpy_file)[0]
with self.assertWarns(DeprecationWarning):
new_circ = load(qpy_file)[0]
self.assertEqual(qc, new_circ)
self.assertEqual(
[x.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
Expand Down Expand Up @@ -755,12 +761,14 @@ def test_single_bit_teleportation(self):
qc = QuantumCircuit(qr, cr, name="Reset Test")
qc.x(0)
qc.measure(0, cr[0])
qc.x(0).c_if(cr[0], 1)
with self.assertWarns(DeprecationWarning):
qc.x(0).c_if(cr[0], 1)
qc.measure(0, cr[1])
qpy_file = io.BytesIO()
dump(qc, qpy_file)
qpy_file.seek(0)
new_circ = load(qpy_file)[0]
with self.assertWarns(DeprecationWarning):
new_circ = load(qpy_file)[0]
self.assertEqual(qc, new_circ)
self.assertEqual(
[x.operation.label for x in qc.data], [x.operation.label for x in new_circ.data]
Expand Down Expand Up @@ -1138,11 +1146,13 @@ def test_qpy_with_for_loop(self):
qc.h(0)
qc.cx(0, 1)
qc.measure(0, 0)
qc.break_loop().c_if(0, True)
with self.assertWarns(DeprecationWarning):
qc.break_loop().c_if(0, True)
qpy_file = io.BytesIO()
dump(qc, qpy_file)
qpy_file.seek(0)
new_circuit = load(qpy_file)[0]
with self.assertWarns(DeprecationWarning):
new_circuit = load(qpy_file)[0]
self.assertEqual(qc, new_circuit)
self.assertDeprecatedBitProperties(qc, new_circuit)

Expand All @@ -1154,11 +1164,13 @@ def test_qpy_with_for_loop_iterator(self):
qc.h(0)
qc.cx(0, 1)
qc.measure(0, 0)
qc.break_loop().c_if(0, True)
with self.assertWarns(DeprecationWarning):
qc.break_loop().c_if(0, True)
qpy_file = io.BytesIO()
dump(qc, qpy_file)
qpy_file.seek(0)
new_circuit = load(qpy_file)[0]
with self.assertWarns(DeprecationWarning):
new_circuit = load(qpy_file)[0]
self.assertEqual(qc, new_circuit)
self.assertDeprecatedBitProperties(qc, new_circuit)

Expand Down
Loading
Loading