# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Circuit operation representing a ``while`` loop."""
from __future__ import annotations
from qiskit.circuit import Clbit, ClassicalRegister, QuantumCircuit
from qiskit.circuit.classical import expr
from qiskit.circuit.exceptions import CircuitError
from ._builder_utils import validate_condition, condition_resources
from .control_flow import ControlFlowOp
[docs]class WhileLoopOp(ControlFlowOp):
"""A circuit operation which repeatedly executes a subcircuit (``body``) until
a condition (``condition``) evaluates as False.
Parameters:
condition: A condition to be checked prior to executing ``body``. Can be
specified as either a tuple of a ``ClassicalRegister`` to be tested
for equality with a given ``int``, or as a tuple of a ``Clbit`` to
be compared to either a ``bool`` or an ``int``.
body: The loop body to be repeatedly executed.
label: An optional label for identifying the instruction.
The classical bits used in ``condition`` must be a subset of those attached
to ``body``.
**Circuit symbol:**
.. parsed-literal::
┌─────────────┐
q_0: ┤0 ├
│ │
q_1: ┤1 ├
│ while_loop │
q_2: ┤2 ├
│ │
c_0: ╡0 ╞
└─────────────┘
"""
def __init__(
self,
condition: tuple[ClassicalRegister, int] | tuple[Clbit, int] | expr.Expr,
body: QuantumCircuit,
label: str | None = None,
):
num_qubits = body.num_qubits
num_clbits = body.num_clbits
super().__init__("while_loop", num_qubits, num_clbits, [body], label=label)
self.condition = validate_condition(condition)
@property
def params(self):
return self._params
@params.setter
def params(self, parameters):
(body,) = parameters
if not isinstance(body, QuantumCircuit):
raise CircuitError(
"WhileLoopOp expects a body parameter of type "
f"QuantumCircuit, but received {type(body)}."
)
if body.num_qubits != self.num_qubits or body.num_clbits != self.num_clbits:
raise CircuitError(
"Attempted to assign a body parameter with a num_qubits or "
"num_clbits different than that of the WhileLoopOp. "
f"WhileLoopOp num_qubits/clbits: {self.num_qubits}/{self.num_clbits} "
f"Supplied body num_qubits/clbits: {body.num_qubits}/{body.num_clbits}."
)
self._params = [body]
@property
def blocks(self):
return (self._params[0],)
[docs] def replace_blocks(self, blocks):
(body,) = blocks
return WhileLoopOp(self.condition, body, label=self.label)
[docs] def c_if(self, classical, val):
raise NotImplementedError(
"WhileLoopOp cannot be classically controlled through Instruction.c_if. "
"Please use an IfElseOp instead."
)
class WhileLoopContext:
"""A context manager for building up while loops onto circuits in a natural order, without
having to construct the loop body first.
Within the block, a lot of the bookkeeping is done for you; you do not need to keep track of
which qubits and clbits you are using, for example. All normal methods of accessing the qubits
on the underlying :obj:`~QuantumCircuit` will work correctly, and resolve into correct accesses
within the interior block.
You generally should never need to instantiate this object directly. Instead, use
:obj:`.QuantumCircuit.while_loop` in its context-manager form, i.e. by not supplying a ``body``
or sets of qubits and clbits.
Example usage::
from qiskit.circuit import QuantumCircuit, Clbit, Qubit
bits = [Qubit(), Qubit(), Clbit()]
qc = QuantumCircuit(bits)
with qc.while_loop((bits[2], 0)):
qc.h(0)
qc.cx(0, 1)
qc.measure(0, 0)
.. warning::
This is an internal interface and no part of it should be relied upon outside of Qiskit
Terra.
"""
__slots__ = ("_circuit", "_condition", "_label")
def __init__(
self,
circuit: QuantumCircuit,
condition: tuple[ClassicalRegister, int] | tuple[Clbit, int] | expr.Expr,
*,
label: str | None = None,
):
self._circuit = circuit
self._condition = validate_condition(condition)
self._label = label
def __enter__(self):
resources = condition_resources(self._condition)
self._circuit._push_scope(clbits=resources.clbits, registers=resources.cregs)
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None:
# If we're leaving the context manager because an exception was raised, there's nothing
# to do except restore the circuit state.
self._circuit._pop_scope()
return False
scope = self._circuit._pop_scope()
# Loops do not need to pass any further resources in, because this scope itself defines the
# extent of ``break`` and ``continue`` statements.
body = scope.build(scope.qubits, scope.clbits)
self._circuit.append(
WhileLoopOp(self._condition, body, label=self._label),
body.qubits,
body.clbits,
)
return False