5151from .controlflow import ControlFlowOp , _builder_utils
5252from .controlflow .builder import CircuitScopeInterface , ControlFlowBuilderBlock
5353from .controlflow .break_loop import BreakLoopOp , BreakLoopPlaceholder
54+ from .controlflow .box import BoxOp , BoxContext
5455from .controlflow .continue_loop import ContinueLoopOp , ContinueLoopPlaceholder
5556from .controlflow .for_loop import ForLoopOp , ForLoopContext
5657from .controlflow .if_else import IfElseOp , IfContext
@@ -737,13 +738,14 @@ class QuantumCircuit:
737738 ============================== ================================================================
738739 :class:`QuantumCircuit` method Control-flow instruction
739740 ============================== ================================================================
740- :meth:`if_test` :class:`.IfElseOp` with only a ``True`` body.
741- :meth:`if_else` :class:`.IfElseOp` with both ``True`` and ``False`` bodies.
742- :meth:`while_loop` :class:`.WhileLoopOp`.
743- :meth:`switch` :class:`.SwitchCaseOp`.
744- :meth:`for_loop` :class:`.ForLoopOp`.
745- :meth:`break_loop` :class:`.BreakLoopOp`.
746- :meth:`continue_loop` :class:`.ContinueLoopOp`.
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:`box` :class:`.BoxOp`
747+ :meth:`break_loop` :class:`.BreakLoopOp`
748+ :meth:`continue_loop` :class:`.ContinueLoopOp`
747749 ============================== ================================================================
748750
749751 :class:`QuantumCircuit` has corresponding methods for all of the control-flow operations that
@@ -769,6 +771,7 @@ class QuantumCircuit:
769771 ..
770772 TODO: expand the examples of the builder interface.
771773
774+ .. automethod:: box
772775 .. automethod:: break_loop
773776 .. automethod:: continue_loop
774777 .. automethod:: for_loop
@@ -6040,6 +6043,107 @@ def _pop_previous_instruction_in_scope(self) -> CircuitInstruction:
60406043 instruction = self ._data .pop ()
60416044 return instruction
60426045
6046+ def box (
6047+ self ,
6048+ # Forbidding passing `body` by keyword is in anticipation of the constructor expanding to
6049+ # allow `annotations` to be passed as the positional argument in the context-manager form.
6050+ body : QuantumCircuit | None = None ,
6051+ / ,
6052+ qubits : Sequence [QubitSpecifier ] | None = None ,
6053+ clbits : Sequence [ClbitSpecifier ] | None = None ,
6054+ * ,
6055+ label : str | None = None ,
6056+ duration : None = None ,
6057+ unit : Literal ["dt" , "s" , "ms" , "us" , "ns" , "ps" ] = "dt" ,
6058+ ):
6059+ """Create a ``box`` of operations on this circuit that are treated atomically in the greater
6060+ context.
6061+
6062+ A "box" is a control-flow construct that is entered unconditionally. The contents of the
6063+ box behave somewhat as if the start and end of the box were barriers (see :meth:`barrier`),
6064+ except it is permissible to commute operations "all the way" through the box. The box is
6065+ also an explicit scope for the purposes of variables, stretches and compiler passes.
6066+
6067+ There are two forms for calling this function:
6068+
6069+ * Pass a :class:`QuantumCircuit` positionally, and the ``qubits`` and ``clbits`` it acts
6070+ on. In this form, a :class:`.BoxOp` is immediately created and appended using the circuit
6071+ as the body.
6072+
6073+ * Use in a ``with`` statement with no ``body``, ``qubits`` or ``clbits``. This is the
6074+ "builder-interface form", where you then use other :class:`QuantumCircuit` methods within
6075+ the Python ``with`` scope to add instructions to the ``box``. This is the preferred form,
6076+ and much less error prone.
6077+
6078+ Examples:
6079+
6080+ Using the builder interface to add two boxes in sequence. The two boxes in this circuit
6081+ can execute concurrently, and the second explicitly inserts a data-flow dependency on
6082+ qubit 8 for the duration of the box, even though the qubit is idle.
6083+
6084+ .. code-block:: python
6085+
6086+ from qiskit.circuit import QuantumCircuit
6087+
6088+ qc = QuantumCircuit(9)
6089+ with qc.box():
6090+ qc.cz(0, 1)
6091+ qc.cz(2, 3)
6092+ with qc.box():
6093+ qc.cz(4, 5)
6094+ qc.cz(6, 7)
6095+ qc.noop(8)
6096+
6097+ Using the explicit construction of box. This creates the same circuit as above, and
6098+ should give an indication why the previous form is preferred for interactive use.
6099+
6100+ .. code-block:: python
6101+
6102+ from qiskit.circuit import QuantumCircuit, BoxOp
6103+
6104+ body_0 = QuantumCircuit(4)
6105+ body_0.cz(0, 1)
6106+ body_0.cz(2, 3)
6107+
6108+ # Note that the qubit indices inside a body related only to the body. The
6109+ # association with qubits in the containing circuit is made by the ``qubits``
6110+ # argument to `QuantumCircuit.box`.
6111+ body_1 = QuantumCircuit(5)
6112+ body_1.cz(0, 1)
6113+ body_1.cz(2, 3)
6114+
6115+ qc = QuantumCircuit(9)
6116+ qc.box(body_0, [0, 1, 2, 3], [])
6117+ qc.box(body_1, [4, 5, 6, 7, 8], [])
6118+
6119+ Args:
6120+ body: if given, the :class:`QuantumCircuit` to use as the box's body in the explicit
6121+ construction. Not given in the context-manager form.
6122+ qubits: the qubits to apply the :class:`.BoxOp` to, in the explicit form.
6123+ clbits: the qubits to apply the :class:`.BoxOp` to, in the explicit form.
6124+ label: an optional string label for the instruction.
6125+ duration: an optional explicit duration for the :class:`.BoxOp`. Scheduling passes are
6126+ constrained to schedule the contained scope to match a given duration, including
6127+ delay insertion if required.
6128+ unit: the unit of the ``duration``.
6129+ """
6130+ if isinstance (body , QuantumCircuit ):
6131+ # Explicit-body form.
6132+ if qubits is None or clbits is None :
6133+ raise CircuitError ("When using 'box' with a body, you must pass qubits and clbits." )
6134+ return self .append (
6135+ BoxOp (body , duration = duration , unit = unit , label = label ),
6136+ qubits ,
6137+ clbits ,
6138+ copy = False ,
6139+ )
6140+ # Context-manager form.
6141+ if qubits is not None or clbits is not None :
6142+ raise CircuitError (
6143+ "When using 'box' as a context manager, you cannot pass qubits or clbits."
6144+ )
6145+ return BoxContext (self , duration = duration , unit = unit , label = label )
6146+
60436147 @typing .overload
60446148 def while_loop (
60456149 self ,
0 commit comments