5252from .controlflow import ControlFlowOp , _builder_utils
5353from .controlflow .builder import CircuitScopeInterface , ControlFlowBuilderBlock
5454from .controlflow .break_loop import BreakLoopOp , BreakLoopPlaceholder
55+ from .controlflow .box import BoxOp , BoxContext
5556from .controlflow .continue_loop import ContinueLoopOp , ContinueLoopPlaceholder
5657from .controlflow .for_loop import ForLoopOp , ForLoopContext
5758from .controlflow .if_else import IfElseOp , IfContext
@@ -738,13 +739,14 @@ class QuantumCircuit:
738739 ============================== ================================================================
739740 :class:`QuantumCircuit` method Control-flow instruction
740741 ============================== ================================================================
741- :meth:`if_test` :class:`.IfElseOp` with only a ``True`` body.
742- :meth:`if_else` :class:`.IfElseOp` with both ``True`` and ``False`` bodies.
743- :meth:`while_loop` :class:`.WhileLoopOp`.
744- :meth:`switch` :class:`.SwitchCaseOp`.
745- :meth:`for_loop` :class:`.ForLoopOp`.
746- :meth:`break_loop` :class:`.BreakLoopOp`.
747- :meth:`continue_loop` :class:`.ContinueLoopOp`.
742+ :meth:`if_test` :class:`.IfElseOp` with only a ``True`` body
743+ :meth:`if_else` :class:`.IfElseOp` with both ``True`` and ``False`` bodies
744+ :meth:`while_loop` :class:`.WhileLoopOp`
745+ :meth:`switch` :class:`.SwitchCaseOp`
746+ :meth:`for_loop` :class:`.ForLoopOp`
747+ :meth:`box` :class:`.BoxOp`
748+ :meth:`break_loop` :class:`.BreakLoopOp`
749+ :meth:`continue_loop` :class:`.ContinueLoopOp`
748750 ============================== ================================================================
749751
750752 :class:`QuantumCircuit` has corresponding methods for all of the control-flow operations that
@@ -770,6 +772,7 @@ class QuantumCircuit:
770772 ..
771773 TODO: expand the examples of the builder interface.
772774
775+ .. automethod:: box
773776 .. automethod:: break_loop
774777 .. automethod:: continue_loop
775778 .. automethod:: for_loop
@@ -6342,6 +6345,107 @@ def _pop_previous_instruction_in_scope(self) -> CircuitInstruction:
63426345 instruction = self ._data .pop ()
63436346 return instruction
63446347
6348+ def box (
6349+ self ,
6350+ # Forbidding passing `body` by keyword is in anticipation of the constructor expanding to
6351+ # allow `annotations` to be passed as the positional argument in the context-manager form.
6352+ body : QuantumCircuit | None = None ,
6353+ / ,
6354+ qubits : Sequence [QubitSpecifier ] | None = None ,
6355+ clbits : Sequence [ClbitSpecifier ] | None = None ,
6356+ * ,
6357+ label : str | None = None ,
6358+ duration : None = None ,
6359+ unit : Literal ["dt" , "s" , "ms" , "us" , "ns" , "ps" ] = "dt" ,
6360+ ):
6361+ """Create a ``box`` of operations on this circuit that are treated atomically in the greater
6362+ context.
6363+
6364+ A "box" is a control-flow construct that is entered unconditionally. The contents of the
6365+ box behave somewhat as if the start and end of the box were barriers (see :meth:`barrier`),
6366+ except it is permissible to commute operations "all the way" through the box. The box is
6367+ also an explicit scope for the purposes of variables, stretches and compiler passes.
6368+
6369+ There are two forms for calling this function:
6370+
6371+ * Pass a :class:`QuantumCircuit` positionally, and the ``qubits`` and ``clbits`` it acts
6372+ on. In this form, a :class:`.BoxOp` is immediately created and appended using the circuit
6373+ as the body.
6374+
6375+ * Use in a ``with`` statement with no ``body``, ``qubits`` or ``clbits``. This is the
6376+ "builder-interface form", where you then use other :class:`QuantumCircuit` methods within
6377+ the Python ``with`` scope to add instructions to the ``box``. This is the preferred form,
6378+ and much less error prone.
6379+
6380+ Examples:
6381+
6382+ Using the builder interface to add two boxes in sequence. The two boxes in this circuit
6383+ can execute concurrently, and the second explicitly inserts a data-flow dependency on
6384+ qubit 8 for the duration of the box, even though the qubit is idle.
6385+
6386+ .. code-block:: python
6387+
6388+ from qiskit.circuit import QuantumCircuit
6389+
6390+ qc = QuantumCircuit(9)
6391+ with qc.box():
6392+ qc.cz(0, 1)
6393+ qc.cz(2, 3)
6394+ with qc.box():
6395+ qc.cz(4, 5)
6396+ qc.cz(6, 7)
6397+ qc.noop(8)
6398+
6399+ Using the explicit construction of box. This creates the same circuit as above, and
6400+ should give an indication why the previous form is preferred for interactive use.
6401+
6402+ .. code-block:: python
6403+
6404+ from qiskit.circuit import QuantumCircuit, BoxOp
6405+
6406+ body_0 = QuantumCircuit(4)
6407+ body_0.cz(0, 1)
6408+ body_0.cz(2, 3)
6409+
6410+ # Note that the qubit indices inside a body related only to the body. The
6411+ # association with qubits in the containing circuit is made by the ``qubits``
6412+ # argument to `QuantumCircuit.box`.
6413+ body_1 = QuantumCircuit(5)
6414+ body_1.cz(0, 1)
6415+ body_1.cz(2, 3)
6416+
6417+ qc = QuantumCircuit(9)
6418+ qc.box(body_0, [0, 1, 2, 3], [])
6419+ qc.box(body_1, [4, 5, 6, 7, 8], [])
6420+
6421+ Args:
6422+ body: if given, the :class:`QuantumCircuit` to use as the box's body in the explicit
6423+ construction. Not given in the context-manager form.
6424+ qubits: the qubits to apply the :class:`.BoxOp` to, in the explicit form.
6425+ clbits: the qubits to apply the :class:`.BoxOp` to, in the explicit form.
6426+ label: an optional string label for the instruction.
6427+ duration: an optional explicit duration for the :class:`.BoxOp`. Scheduling passes are
6428+ constrained to schedule the contained scope to match a given duration, including
6429+ delay insertion if required.
6430+ unit: the unit of the ``duration``.
6431+ """
6432+ if isinstance (body , QuantumCircuit ):
6433+ # Explicit-body form.
6434+ if qubits is None or clbits is None :
6435+ raise CircuitError ("When using 'box' with a body, you must pass qubits and clbits." )
6436+ return self .append (
6437+ BoxOp (body , duration = duration , unit = unit , label = label ),
6438+ qubits ,
6439+ clbits ,
6440+ copy = False ,
6441+ )
6442+ # Context-manager form.
6443+ if qubits is not None or clbits is not None :
6444+ raise CircuitError (
6445+ "When using 'box' as a context manager, you cannot pass qubits or clbits."
6446+ )
6447+ return BoxContext (self , duration = duration , unit = unit , label = label )
6448+
63456449 @typing .overload
63466450 def while_loop (
63476451 self ,
0 commit comments