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
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
2 changes: 1 addition & 1 deletion crates/circuit/src/circuit_instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ impl<'py> FromPyObject<'py> for OperationFromPython {
ob.getattr(intern!(py, "label"))?.extract()?,
duration,
unit,
ob.getattr(intern!(py, "condition"))?.extract()?,
ob.getattr(intern!(py, "_condition"))?.extract()?,
))
};

Expand Down
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.")
6 changes: 3 additions & 3 deletions qiskit/circuit/controlflow/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ def _copy_mutable_properties(self, instruction: Instruction) -> Instruction:
The same instruction instance that was passed, but mutated to propagate the tracked
changes to this class.
"""
instruction.condition = self.condition
instruction._condition = self._condition
return instruction

# Provide some better error messages, just in case something goes wrong during development and
Expand Down Expand Up @@ -639,8 +639,8 @@ def update_registers(index, op):
# a register is already present, so we use our own tracking.
self.add_register(register)
out.add_register(register)
if getattr(op, "condition", None) is not None:
for register in condition_resources(op.condition).cregs:
if getattr(op, "_condition", None) is not None:
for register in condition_resources(op._condition).cregs:
if register not in self.registers:
self.add_register(register)
out.add_register(register)
Expand Down
18 changes: 13 additions & 5 deletions qiskit/circuit/controlflow/if_else.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,20 @@ def __init__(

super().__init__("if_else", num_qubits, num_clbits, [true_body, false_body], label=label)

self.condition = validate_condition(condition)
self._condition = validate_condition(condition)

@property
def params(self):
return self._params

@property
def condition(self):
return self._condition

@condition.setter
def condition(self, value):
self._condition = value

@params.setter
def params(self, parameters):
# pylint: disable=cyclic-import
Expand Down Expand Up @@ -152,7 +160,7 @@ def replace_blocks(self, blocks: Iterable[QuantumCircuit]) -> "IfElseOp":
true_body, false_body = (
ablock for ablock, _ in itertools.zip_longest(blocks, range(2), fillvalue=None)
)
return IfElseOp(self.condition, true_body, false_body=false_body, label=self.label)
return IfElseOp(self._condition, true_body, false_body=false_body, label=self.label)

def c_if(self, classical, val):
raise NotImplementedError(
Expand Down Expand Up @@ -200,7 +208,7 @@ def __init__(
"if_else", len(self.__resources.qubits), len(self.__resources.clbits), [], label=label
)
# Set the condition after super().__init__() has initialized it to None.
self.condition = validate_condition(condition)
self._condition = validate_condition(condition)

def with_false_block(self, false_block: ControlFlowBuilderBlock) -> "IfElsePlaceholder":
"""Return a new placeholder instruction, with the false block set to the given value,
Expand All @@ -225,7 +233,7 @@ def with_false_block(self, false_block: ControlFlowBuilderBlock) -> "IfElsePlace
false_bits = false_block.qubits() | false_block.clbits()
true_block.add_bits(false_bits - true_bits)
false_block.add_bits(true_bits - false_bits)
return type(self)(self.condition, true_block, false_block, label=self.label)
return type(self)(self._condition, true_block, false_block, label=self.label)

def registers(self):
"""Get the registers used by the interior blocks."""
Expand Down Expand Up @@ -288,7 +296,7 @@ def concrete_instruction(self, qubits, clbits):
)
return (
self._copy_mutable_properties(
IfElseOp(self.condition, true_body, false_body, label=self.label)
IfElseOp(self._condition, true_body, false_body, label=self.label)
),
InstructionResources(
qubits=tuple(true_body.qubits),
Expand Down
12 changes: 10 additions & 2 deletions qiskit/circuit/controlflow/while_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,20 @@ def __init__(
num_clbits = body.num_clbits

super().__init__("while_loop", num_qubits, num_clbits, [body], label=label)
self.condition = validate_condition(condition)
self._condition = validate_condition(condition)

@property
def params(self):
return self._params

@property
def condition(self):
return self._condition

@condition.setter
def condition(self, value):
self._condition = value

@params.setter
def params(self, parameters):
# pylint: disable=cyclic-import
Expand Down Expand Up @@ -88,7 +96,7 @@ def blocks(self):

def replace_blocks(self, blocks):
(body,) = blocks
return WhileLoopOp(self.condition, body, label=self.label)
return WhileLoopOp(self._condition, body, label=self.label)

def c_if(self, classical, val):
raise NotImplementedError(
Expand Down
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 Down Expand Up @@ -46,6 +47,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
17 changes: 10 additions & 7 deletions qiskit/circuit/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def to_mutable(self):
return self.copy()

@property
@deprecate_func(since="1.3.0", removal_timeline="in 2.0.0", is_property=True)
def condition(self):
"""The classical condition on the instruction."""
return self._condition
Expand Down Expand Up @@ -410,8 +411,8 @@ def _assemble(self):
# Add condition parameters for assembler. This is needed to convert
# to a qobj conditional instruction at assemble time and after
# conversion will be deleted by the assembler.
if self.condition:
instruction._condition = self.condition
if self._condition:
instruction._condition = self._condition
return instruction

@property
Expand Down Expand Up @@ -517,6 +518,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 Expand Up @@ -632,26 +634,27 @@ def repeat(self, n):
qargs = tuple(qc.qubits)
cargs = tuple(qc.clbits)
base = self.copy()
if self.condition:
if self._condition:
# Condition is handled on the outer instruction.
base = base.to_mutable()
base.condition = None
for _ in [None] * n:
qc._append(CircuitInstruction(base, qargs, cargs))

instruction.definition = qc
if self.condition:
instruction = instruction.c_if(*self.condition)
if self._condition:
instruction = instruction.c_if(*self._condition)
return instruction

@property
@deprecate_func(since="1.3.0", removal_timeline="in 2.0.0", is_property=True)
def condition_bits(self) -> List[Clbit]:
"""Get Clbits in condition."""
from qiskit.circuit.controlflow import condition_resources # pylint: disable=cyclic-import

if self.condition is None:
if self._condition is None:
return []
return list(condition_resources(self.condition).clbits)
return list(condition_resources(self._condition).clbits)

@property
def name(self):
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
8 changes: 4 additions & 4 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2102,14 +2102,14 @@ def map_vars(op):
is_control_flow = isinstance(n_op, ControlFlowOp)
if (
not is_control_flow
and (condition := getattr(n_op, "condition", None)) is not None
and (condition := getattr(n_op, "_condition", None)) is not None
):
n_op = n_op.copy() if n_op is op and copy else n_op
n_op.condition = variable_mapper.map_condition(condition)
elif is_control_flow:
n_op = n_op.replace_blocks(recurse_block(block) for block in n_op.blocks)
if isinstance(n_op, (IfElseOp, WhileLoopOp)):
n_op.condition = variable_mapper.map_condition(n_op.condition)
n_op.condition = variable_mapper.map_condition(n_op._condition)
elif isinstance(n_op, SwitchCaseOp):
n_op.target = variable_mapper.map_target(n_op.target)
elif isinstance(n_op, Store):
Expand Down Expand Up @@ -3520,7 +3520,7 @@ def update_from_expr(objects, node):

for instruction in self._data:
objects = set(itertools.chain(instruction.qubits, instruction.clbits))
if (condition := getattr(instruction.operation, "condition", None)) is not None:
if (condition := getattr(instruction.operation, "_condition", None)) is not None:
objects.update(_builder_utils.condition_resources(condition).clbits)
if isinstance(condition, expr.Expr):
update_from_expr(objects, condition)
Expand Down Expand Up @@ -3623,7 +3623,7 @@ def num_connected_components(self, unitary_only: bool = False) -> int:
else:
args = instruction.qubits + instruction.clbits
num_qargs = len(args) + (
1 if getattr(instruction.operation, "condition", None) else 0
1 if getattr(instruction.operation, "_condition", None) else 0
)

if num_qargs >= 2 and not getattr(instruction.operation, "_directive", False):
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
2 changes: 1 addition & 1 deletion qiskit/converters/circuit_to_instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def fix_condition(op):
if (out := operation_map.get(original_id)) is not None:
return out

condition = getattr(op, "condition", None)
condition = getattr(op, "_condition", None)
if condition:
reg, val = condition
if isinstance(reg, Clbit):
Expand Down
6 changes: 3 additions & 3 deletions qiskit/dagcircuit/collect_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def split_block_into_layers(block: list[DAGOpNode | DAGDepNode]):
cur_bits = set(node.qargs)
cur_bits.update(node.cargs)

cond = getattr(node.op, "condition", None)
cond = getattr(node.op, "_condition", None)
if cond is not None:
cur_bits.update(condition_resources(cond).clbits)

Expand Down Expand Up @@ -356,7 +356,7 @@ def collapse_to_operation(self, blocks, collapse_fn):
for node in block:
cur_qubits.update(node.qargs)
cur_clbits.update(node.cargs)
cond = getattr(node.op, "condition", None)
cond = getattr(node.op, "_condition", None)
if cond is not None:
cur_clbits.update(condition_resources(cond).clbits)
if isinstance(cond[0], ClassicalRegister):
Expand All @@ -378,7 +378,7 @@ def collapse_to_operation(self, blocks, collapse_fn):

for node in block:
instructions = qc.append(CircuitInstruction(node.op, node.qargs, node.cargs))
cond = getattr(node.op, "condition", None)
cond = getattr(node.op, "_condition", None)
if cond is not None:
instructions.c_if(*cond)

Expand Down
6 changes: 3 additions & 3 deletions qiskit/dagcircuit/dagdependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,13 +409,13 @@ def _create_op_node(self, operation, qargs, cargs):
for elem in qargs:
qindices_list.append(self.qubits.index(elem))

if getattr(operation, "condition", None):
if getattr(operation, "_condition", None):
# The change to handling operation.condition follows code patterns in quantum_circuit.py.
# However:
# (1) cindices_list are specific to template optimization and should not be computed
# in this place.
# (2) Template optimization pass needs currently does not handle general conditions.
cond_bits = condition_resources(operation.condition).clbits
cond_bits = condition_resources(operation._condition).clbits
cindices_list = [self.clbits.index(clbit) for clbit in cond_bits]
else:
cindices_list = []
Expand Down Expand Up @@ -609,7 +609,7 @@ def replace_block_with_op(self, node_block, op, wire_pos_map, cycle_check=True):
for nd in node_block:
block_qargs |= set(nd.qargs)
block_cargs |= set(nd.cargs)
cond = getattr(nd.op, "condition", None)
cond = getattr(nd.op, "_condition", None)
if cond is not None:
block_cargs.update(condition_resources(cond).clbits)

Expand Down
4 changes: 2 additions & 2 deletions qiskit/dagcircuit/dagnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ def key(var):


def _condition_op_eq(node1, node2, bit_indices1, bit_indices2):
cond1 = node1.op.condition
cond2 = node2.op.condition
cond1 = node1.condition
cond2 = node2.condition
if isinstance(cond1, expr.Expr) and isinstance(cond2, expr.Expr):
if not expr.structurally_equivalent(
cond1, cond2, _make_expr_key(bit_indices1), _make_expr_key(bit_indices2)
Expand Down
2 changes: 1 addition & 1 deletion qiskit/primitives/statevector_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,6 @@ def _final_measurement_mapping(circuit: QuantumCircuit) -> dict[tuple[ClassicalR

def _has_control_flow(circuit: QuantumCircuit) -> bool:
return any(
isinstance((op := instruction.operation), ControlFlowOp) or op.condition
isinstance((op := instruction.operation), ControlFlowOp) or op._condition
for instruction in circuit
)
8 changes: 5 additions & 3 deletions qiskit/qasm2/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,14 @@ def _instruction_call_site(operation):
if operation.params:
params = ",".join([pi_check(i, output="qasm", eps=1e-12) for i in operation.params])
qasm2_call = f"{qasm2_call}({params})"
if operation.condition is not None:
if not isinstance(operation.condition[0], ClassicalRegister):
if operation._condition is not None:
if not isinstance(operation._condition[0], ClassicalRegister):
raise QASM2ExportError(
"OpenQASM 2 can only condition on registers, but got '{operation.condition[0]}'"
)
qasm2_call = f"if({operation.condition[0].name}=={operation.condition[1]:d}) " + qasm2_call
qasm2_call = (
f"if({operation._condition[0].name}=={operation._condition[1]:d}) " + qasm2_call
)
return qasm2_call


Expand Down
Loading