From 3f0b5e3f98bc4e4912cb9ee9e565d9369236aa21 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 15:32:40 +0000 Subject: [PATCH] Write complete manual `QuantumCircuit` documentation (#12403) (#12420) * Write complete manual `QuantumCircuit` documentation This writes huge tracts of new `QuantumCircuit` API documentation, linking together alike methods and writing explanatory text for how all the components fit together. There's likely an awful lot more that could go into this too, but this hopefully should impose a lot more order on the huge `QuantumCircuit` documentation page, and provide a lot more explanation for how the class works holistically. In particular, the section on the control-flow builder interface could do with a lot more exposition and examples right now. * Reword from review Co-authored-by: Matthew Treinish * Add missing text around `Var` methods * Add example to `find_bit` * Comment on metadata through serialization * Add see-also sections to undesirable methods * Re-add internal utilities to documentation * Add examples to `depth` --------- Co-authored-by: Matthew Treinish (cherry picked from commit 72ad545a87e1cbd79afe4bbc64f5b835a8e92f7e) Co-authored-by: Jake Lishman --- docs/apidoc/index.rst | 1 + docs/apidoc/qiskit.circuit.QuantumCircuit.rst | 17 + qiskit/circuit/__init__.py | 163 +-- qiskit/circuit/quantumcircuit.py | 1216 +++++++++++++++-- 4 files changed, 1098 insertions(+), 299 deletions(-) create mode 100644 docs/apidoc/qiskit.circuit.QuantumCircuit.rst diff --git a/docs/apidoc/index.rst b/docs/apidoc/index.rst index 30bb20998d6b..8581d56ace77 100644 --- a/docs/apidoc/index.rst +++ b/docs/apidoc/index.rst @@ -13,6 +13,7 @@ Circuit construction: :maxdepth: 1 circuit + qiskit.circuit.QuantumCircuit circuit_classical classicalfunction circuit_library diff --git a/docs/apidoc/qiskit.circuit.QuantumCircuit.rst b/docs/apidoc/qiskit.circuit.QuantumCircuit.rst new file mode 100644 index 000000000000..1fa9cb5a7d9c --- /dev/null +++ b/docs/apidoc/qiskit.circuit.QuantumCircuit.rst @@ -0,0 +1,17 @@ +.. _qiskit-circuit-quantumcircuit: + +============================== +:class:`.QuantumCircuit` class +============================== + +.. + This is so big it gets its own page in the toctree, and because we + don't want it to use autosummary. + +.. currentmodule:: qiskit.circuit + +.. autoclass:: qiskit.circuit.QuantumCircuit + :no-members: + :no-inherited-members: + :no-special-members: + :class-doc-from: class diff --git a/qiskit/circuit/__init__.py b/qiskit/circuit/__init__.py index ff36550967d8..3982fa873349 100644 --- a/qiskit/circuit/__init__.py +++ b/qiskit/circuit/__init__.py @@ -321,16 +321,6 @@ :class:`QuantumCircuit` class itself and the multitude of available methods on it in its class documentation. -.. - TODO: the intention is to replace this `autosummary` directive with a proper entry in the API - toctree once the `QuantumCircuit` class-level documentation has been completely rewritten into - more of this style. For now, this just ensures it gets *any* page generated. - -.. autosummary:: - :toctree: ../stubs/ - - QuantumCircuit - Internally, a :class:`QuantumCircuit` contains the qubits, classical bits, compile-time parameters, real-time variables, and other tracking information about the data it acts on and how it is parametrized. It then contains a sequence of :class:`CircuitInstruction`\ s, which contain @@ -390,7 +380,7 @@ Circuits track registers, but registers themselves impart almost no behavioral differences on circuits. The only exception is that :class:`ClassicalRegister`\ s can be implicitly cast to unsigned integers for use in conditional comparisons of :ref:`control flow operations -`. +`. Classical registers and bits were the original way of representing classical data in Qiskit, and remain the most supported currently. Longer term, the data model is moving towards a more complete @@ -433,6 +423,8 @@ circuit), but these are now discouraged and you should use the alternatives noted in those methods. +.. _circuit-operations-instructions: + Operations, instructions and gates ---------------------------------- @@ -598,17 +590,14 @@ Real-time classical computation ------------------------------- -.. note:: +.. seealso:: + :mod:`qiskit.circuit.classical` + Module-level documentation for how the variable-, expression- and type-systems work, the + objects used to represent them, and the classical operations available. - The primary documentation for real-time classical computation is in the module-level - documentation of :mod:`qiskit.circuit.classical`. - - You might also want to read about the circuit methods for working with real-time variables on - the :class:`QuantumCircuit` class page. - - .. - TODO: write a section in the QuantumCircuit-level guide about real-time-variable methods and - cross-ref to it. + :ref:`circuit-real-time-methods` + The :class:`QuantumCircuit` methods for working with these variables in the context of a + single circuit. Qiskit has rudimentary low-level support for representing real-time classical computations, which happen during the QPU execution and affect the results. We are still relatively early into hardware @@ -674,7 +663,7 @@ ParameterVector -.. _circuit-control-flow: +.. _circuit-control-flow-repr: Control flow in circuits ------------------------ @@ -718,11 +707,8 @@ The classes representations are documented here, but please note that manually constructing these classes is a low-level operation that we do not expect users to need to do frequently. - .. - TODO: make this below statement valid, and reinsert. - - Users should read :ref:`circuit-creating-control-flow` for the recommended workflows for - building control-flow-enabled circuits. + Users should read :ref:`circuit-control-flow-methods` for the recommended workflows for building + control-flow-enabled circuits. Since :class:`ControlFlowOp` subclasses are also :class:`Instruction` subclasses, this means that the way they are stored in :class:`CircuitInstruction` instances has them "applied" to a sequence of @@ -772,11 +758,8 @@ argument), but user code will typically use the control-flow builder interface, which handles this automatically. -.. - TODO: make the below sentence valid, then re-insert. - - Consult :ref:`the control-flow construction documentation ` for - more information on how to build circuits with control flow. +Consult :ref:`the control-flow construction documentation ` for more +information on how to build circuits with control flow. .. _circuit-custom-gates: @@ -920,122 +903,6 @@ def __array__(self, dtype=None, copy=None): Working with circuit-level objects ================================== -Circuit properties ------------------- - -.. - TODO: rewrite this section and move it into the `QuantumCircuit` class-level overview of its - functions. - -When constructing quantum circuits, there are several properties that help quantify -the "size" of the circuits, and their ability to be run on a noisy quantum device. -Some of these, like number of qubits, are straightforward to understand, while others -like depth and number of tensor components require a bit more explanation. Here we will -explain all of these properties, and, in preparation for understanding how circuits change -when run on actual devices, highlight the conditions under which they change. - -Consider the following circuit: - -.. plot:: - :include-source: - - from qiskit import QuantumCircuit - qc = QuantumCircuit(12) - for idx in range(5): - qc.h(idx) - qc.cx(idx, idx+5) - - qc.cx(1, 7) - qc.x(8) - qc.cx(1, 9) - qc.x(7) - qc.cx(1, 11) - qc.swap(6, 11) - qc.swap(6, 9) - qc.swap(6, 10) - qc.x(6) - qc.draw('mpl') - -From the plot, it is easy to see that this circuit has 12 qubits, and a collection of -Hadamard, CNOT, X, and SWAP gates. But how to quantify this programmatically? Because we -can do single-qubit gates on all the qubits simultaneously, the number of qubits in this -circuit is equal to the **width** of the circuit: - -.. code-block:: - - qc.width() - -.. parsed-literal:: - - 12 - -We can also just get the number of qubits directly: - -.. code-block:: - - qc.num_qubits - -.. parsed-literal:: - - 12 - -.. important:: - - For a quantum circuit composed from just qubits, the circuit width is equal - to the number of qubits. This is the definition used in quantum computing. However, - for more complicated circuits with classical registers, and classically controlled gates, - this equivalence breaks down. As such, from now on we will not refer to the number of - qubits in a quantum circuit as the width. - - -It is also straightforward to get the number and type of the gates in a circuit using -:meth:`QuantumCircuit.count_ops`: - -.. code-block:: - - qc.count_ops() - -.. parsed-literal:: - - OrderedDict([('cx', 8), ('h', 5), ('x', 3), ('swap', 3)]) - -We can also get just the raw count of operations by computing the circuits -:meth:`QuantumCircuit.size`: - -.. code-block:: - - qc.size() - -.. parsed-literal:: - - 19 - -A particularly important circuit property is known as the circuit **depth**. The depth -of a quantum circuit is a measure of how many "layers" of quantum gates, executed in -parallel, it takes to complete the computation defined by the circuit. Because quantum -gates take time to implement, the depth of a circuit roughly corresponds to the amount of -time it takes the quantum computer to execute the circuit. Thus, the depth of a circuit -is one important quantity used to measure if a quantum circuit can be run on a device. - -The depth of a quantum circuit has a mathematical definition as the longest path in a -directed acyclic graph (DAG). However, such a definition is a bit hard to grasp, even for -experts. Fortunately, the depth of a circuit can be easily understood by anyone familiar -with playing `Tetris `_. Lets see how to compute this -graphically: - -.. image:: /source_images/depth.gif - - -We can verify our graphical result using :meth:`QuantumCircuit.depth`: - -.. code-block:: - - qc.depth() - -.. parsed-literal:: - - 9 - .. _circuit-abstract-to-physical: Converting abstract circuits to physical circuits diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index abd48c686b15..a157f04375a3 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -106,113 +106,878 @@ BitType = TypeVar("BitType", Qubit, Clbit) +# NOTE: +# +# If you're adding methods or attributes to `QuantumCircuit`, be sure to update the class docstring +# to document them in a suitable place. The class is huge, so we do its documentation manually so +# it has at least some amount of organisational structure. + + class QuantumCircuit: - """Create a new circuit. + """Core Qiskit representation of a quantum circuit. + + .. note:: + For more details setting the :class:`QuantumCircuit` in context of all of the data + structures that go with it, how it fits into the rest of the :mod:`qiskit` package, and the + different regimes of quantum-circuit descriptions in Qiskit, see the module-level + documentation of :mod:`qiskit.circuit`. + + Circuit attributes + ================== + + :class:`QuantumCircuit` has a small number of public attributes, which are mostly older + functionality. Most of its functionality is accessed through methods. + + A small handful of the attributes are intentionally mutable, the rest are data attributes that + should be considered immutable. + + ========================= ====================================================================== + Mutable attribute Summary + ========================= ====================================================================== + :attr:`global_phase` The global phase of the circuit, measured in radians. + :attr:`metadata` Arbitrary user mapping, which Qiskit will preserve through the + transpiler, but otherwise completely ignore. + :attr:`name` An optional string name for the circuit. + ========================= ====================================================================== + + ========================= ====================================================================== + Immutable data attribute Summary + ========================= ====================================================================== + :attr:`ancillas` List of :class:`AncillaQubit`\\ s tracked by the circuit. + :attr:`calibrations` Custom user-supplied pulse calibrations for individual instructions. + :attr:`cregs` List of :class:`ClassicalRegister`\\ s tracked by the circuit. + + :attr:`clbits` List of :class:`Clbit`\\ s tracked by the circuit. + :attr:`data` List of individual :class:`CircuitInstruction`\\ s that make up the + circuit. + :attr:`duration` Total duration of the circuit, added by scheduling transpiler passes. + + :attr:`layout` Hardware layout and routing information added by the transpiler. + :attr:`num_ancillas` The number of ancilla qubits in the circuit. + :attr:`num_clbits` The number of clbits in the circuit. + :attr:`num_captured_vars` Number of captured real-time classical variables. + + :attr:`num_declared_vars` Number of locally declared real-time classical variables in the outer + circuit scope. + :attr:`num_input_vars` Number of input real-time classical variables. + :attr:`num_parameters` Number of compile-time :class:`Parameter`\\ s in the circuit. + :attr:`num_qubits` Number of qubits in the circuit. + + :attr:`num_vars` Total number of real-time classical variables in the outer circuit + scope. + :attr:`op_start_times` Start times of scheduled operations, added by scheduling transpiler + passes. + :attr:`parameters` Ordered set-like view of the compile-time :class:`Parameter`\\ s + tracked by the circuit. + :attr:`qregs` List of :class:`QuantumRegister`\\ s tracked by the circuit. + + :attr:`qubits` List of :class:`Qubit`\\ s tracked by the circuit. + :attr:`unit` The unit of the :attr:`duration` field. + ========================= ====================================================================== + + The core attribute is :attr:`data`. This is a sequence-like object that exposes the + :class:`CircuitInstruction`\\ s contained in an ordered form. You generally should not mutate + this object directly; :class:`QuantumCircuit` is only designed for append-only operations (which + should use :meth:`append`). Most operations that mutate circuits in place should be written as + transpiler passes (:mod:`qiskit.transpiler`). + + .. autoattribute:: data + + Alongside the :attr:`data`, the :attr:`global_phase` of a circuit can have some impact on its + output, if the circuit is used to describe a :class:`.Gate` that may be controlled. This is + measured in radians and is directly settable. + + .. autoattribute:: global_phase + + The :attr:`name` of a circuit becomes the name of the :class:`~.circuit.Instruction` or + :class:`.Gate` resulting from :meth:`to_instruction` and :meth:`to_gate` calls, which can be + handy for visualizations. + + .. autoattribute:: name + + You can attach arbitrary :attr:`metadata` to a circuit. No part of core Qiskit will inspect + this or change its behavior based on metadata, but it will be faithfully passed through the + transpiler, so you can tag your circuits yourself. When serializing a circuit with QPY (see + :mod:`qiskit.qpy`), the metadata will be JSON-serialized and you may need to pass a custom + serializer to handle non-JSON-compatible objects within it (see :func:`.qpy.dump` for more + detail). This field is ignored during export to OpenQASM 2 or 3. + + .. autoattribute:: metadata + + :class:`QuantumCircuit` exposes data attributes tracking its internal quantum and classical bits + and registers. These appear as Python :class:`list`\\ s, but you should treat them as + immutable; changing them will *at best* have no effect, and more likely will simply corrupt + the internal data of the :class:`QuantumCircuit`. + + .. autoattribute:: qregs + .. autoattribute:: cregs + .. autoattribute:: qubits + .. autoattribute:: ancillas + .. autoattribute:: clbits + + The :ref:`compile-time parameters ` present in instructions on + the circuit are available in :attr:`parameters`. This has a canonical order (mostly lexical, + except in the case of :class:`.ParameterVector`), which matches the order that parameters will + be assigned when using the list forms of :meth:`assign_parameters`, but also supports + :class:`set`-like constant-time membership testing. + + .. autoattribute:: parameters + + The storage of any :ref:`manual pulse-level calibrations ` for individual + instructions on the circuit is in :attr:`calibrations`. This presents as a :class:`dict`, but + should not be mutated directly; use the methods discussed in :ref:`circuit-calibrations`. + + .. autoattribute:: calibrations + + If you have transpiled your circuit, so you have a physical circuit, you can inspect the + :attr:`layout` attribute for information stored by the transpiler about how the virtual qubits + of the source circuit map to the hardware qubits of your physical circuit, both at the start and + end of the circuit. + + .. autoattribute:: layout + + If your circuit was also *scheduled* as part of a transpilation, it will expose the individual + timings of each instruction, along with the total :attr:`duration` of the circuit. + + .. autoattribute:: duration + .. autoattribute:: unit + .. autoattribute:: op_start_times + + Finally, :class:`QuantumCircuit` exposes several simple properties as dynamic read-only numeric + attributes. + + .. autoattribute:: num_ancillas + .. autoattribute:: num_clbits + .. autoattribute:: num_captured_vars + .. autoattribute:: num_declared_vars + .. autoattribute:: num_input_vars + .. autoattribute:: num_parameters + .. autoattribute:: num_qubits + .. autoattribute:: num_vars + + Creating new circuits + ===================== + + ========================= ===================================================================== + Method Summary + ========================= ===================================================================== + :meth:`__init__` Default constructor of no-instruction circuits. + :meth:`copy` Make a complete copy of an existing circuit. + :meth:`copy_empty_like` Copy data objects from one circuit into a new one without any + instructions. + :meth:`from_instructions` Infer data objects needed from a list of instructions. + :meth:`from_qasm_file` Legacy interface to :func:`.qasm2.load`. + :meth:`from_qasm_str` Legacy interface to :func:`.qasm2.loads`. + ========================= ===================================================================== + + The default constructor (``QuantumCircuit(...)``) produces a circuit with no initial + instructions. The arguments to the default constructor can be used to seed the circuit with + quantum and classical data storage, and to provide a name, global phase and arbitrary metadata. + All of these fields can be expanded later. + + .. automethod:: __init__ + + If you have an existing circuit, you can produce a copy of it using :meth:`copy`, including all + its instructions. This is useful if you want to keep partial circuits while extending another, + or to have a version you can mutate in-place while leaving the prior one intact. - A circuit is a list of instructions bound to some registers. + .. automethod:: copy - Args: - regs (list(:class:`~.Register`) or list(``int``) or list(list(:class:`~.Bit`))): The - registers to be included in the circuit. + Similarly, if you want a circuit that contains all the same data objects (bits, registers, + variables, etc) but with none of the instructions, you can use :meth:`copy_empty_like`. This is + quite common when you want to build up a new layer of a circuit to then use apply onto the back + with :meth:`compose`, or to do a full rewrite of a circuit's instructions. + + .. automethod:: copy_empty_like + + In some cases, it is most convenient to generate a list of :class:`.CircuitInstruction`\\ s + separately to an entire circuit context, and then to build a circuit from this. The + :meth:`from_instructions` constructor will automatically capture all :class:`.Qubit` and + :class:`.Clbit` instances used in the instructions, and create a new :class:`QuantumCircuit` + object that has the correct resources and all the instructions. + + .. automethod:: from_instructions + + :class:`QuantumCircuit` also still has two constructor methods that are legacy wrappers around + the importers in :mod:`qiskit.qasm2`. These automatically apply :ref:`the legacy compatibility + settings ` of :func:`~.qasm2.load` and :func:`~.qasm2.loads`. + + .. automethod:: from_qasm_file + .. automethod:: from_qasm_str + + Data objects on circuits + ======================== - * If a list of :class:`~.Register` objects, represents the :class:`.QuantumRegister` - and/or :class:`.ClassicalRegister` objects to include in the circuit. + .. _circuit-adding-data-objects: + + Adding data objects + ------------------- - For example: + ============================= ================================================================= + Method Adds this kind of data + ============================= ================================================================= + :meth:`add_bits` :class:`.Qubit`\\ s and :class:`.Clbit`\\ s. + :meth:`add_register` :class:`.QuantumRegister` and :class:`.ClassicalRegister`. + :meth:`add_var` :class:`~.expr.Var` nodes with local scope and initializers. + :meth:`add_input` :class:`~.expr.Var` nodes that are treated as circuit inputs. + :meth:`add_capture` :class:`~.expr.Var` nodes captured from containing scopes. + :meth:`add_uninitialized_var` :class:`~.expr.Var` nodes with local scope and undefined state. + ============================= ================================================================= - * ``QuantumCircuit(QuantumRegister(4))`` - * ``QuantumCircuit(QuantumRegister(4), ClassicalRegister(3))`` - * ``QuantumCircuit(QuantumRegister(4, 'qr0'), QuantumRegister(2, 'qr1'))`` + Typically you add most of the data objects (:class:`.Qubit`, :class:`.Clbit`, + :class:`.ClassicalRegister`, etc) to the circuit as part of using the :meth:`__init__` default + constructor, or :meth:`copy_empty_like`. However, it is also possible to add these afterwards. + Typed classical data, such as standalone :class:`~.expr.Var` nodes (see + :ref:`circuit-repr-real-time-classical`), can be both constructed and added with separate + methods. - * If a list of ``int``, the amount of qubits and/or classical bits to include in - the circuit. It can either be a single int for just the number of quantum bits, - or 2 ints for the number of quantum bits and classical bits, respectively. + New registerless :class:`.Qubit` and :class:`.Clbit` objects are added using :meth:`add_bits`. + These objects must not already be present in the circuit. You can check if a bit exists in the + circuit already using :meth:`find_bit`. + + .. automethod:: add_bits + + Registers are added to the circuit with :meth:`add_register`. In this method, it is not an + error if some of the bits are already present in the circuit. In this case, the register will + be an "alias" over the bits. This is not generally well-supported by hardware backends; it is + probably best to stay away from relying on it. The registers a given bit is in are part of the + return of :meth:`find_bit`. - For example: + .. automethod:: add_register - * ``QuantumCircuit(4) # A QuantumCircuit with 4 qubits`` - * ``QuantumCircuit(4, 3) # A QuantumCircuit with 4 qubits and 3 classical bits`` + :ref:`Real-time, typed classical data ` is represented on the + circuit by :class:`~.expr.Var` nodes with a well-defined :class:`~.types.Type`. It is possible + to instantiate these separately to a circuit (see :meth:`.Var.new`), but it is often more + convenient to use circuit methods that will automatically manage the types and expression + initialization for you. The two most common methods are :meth:`add_var` (locally scoped + variables) and :meth:`add_input` (inputs to the circuit). - * If a list of python lists containing :class:`.Bit` objects, a collection of - :class:`.Bit` s to be added to the circuit. + .. automethod:: add_var + .. automethod:: add_input + In addition, there are two lower-level methods that can be useful for programmatic generation of + circuits. When working interactively, you will most likely not need these; most uses of + :meth:`add_uninitialized_var` are part of :meth:`copy_empty_like`, and most uses of + :meth:`add_capture` would be better off using :ref:`the control-flow builder interface + `. - name (str): the name of the quantum circuit. If not set, an - automatically generated string will be assigned. - global_phase (float or ParameterExpression): The global phase of the circuit in radians. - metadata (dict): Arbitrary key value metadata to associate with the - circuit. This gets stored as free-form data in a dict in the - :attr:`~qiskit.circuit.QuantumCircuit.metadata` attribute. It will - not be directly used in the circuit. - inputs: any variables to declare as ``input`` real-time variables for this circuit. These - should already be existing :class:`.expr.Var` nodes that you build from somewhere else; - if you need to create the inputs as well, use :meth:`QuantumCircuit.add_input`. The - variables given in this argument will be passed directly to :meth:`add_input`. A - circuit cannot have both ``inputs`` and ``captures``. - captures: any variables that that this circuit scope should capture from a containing scope. - The variables given here will be passed directly to :meth:`add_capture`. A circuit - cannot have both ``inputs`` and ``captures``. - declarations: any variables that this circuit should declare and initialize immediately. - You can order this input so that later declarations depend on earlier ones (including - inputs or captures). If you need to depend on values that will be computed later at - runtime, use :meth:`add_var` at an appropriate point in the circuit execution. + .. automethod:: add_uninitialized_var + .. automethod:: add_capture - This argument is intended for convenient circuit initialization when you already have a - set of created variables. The variables used here will be directly passed to - :meth:`add_var`, which you can use directly if this is the first time you are creating - the variable. + Working with bits and registers + ------------------------------- - Raises: - CircuitError: if the circuit name, if given, is not valid. - CircuitError: if both ``inputs`` and ``captures`` are given. + A :class:`.Bit` instance is, on its own, just a unique handle for circuits to use in their own + contexts. If you have got a :class:`.Bit` instance and a cirucit, just can find the contexts + that the bit exists in using :meth:`find_bit`, such as its integer index in the circuit and any + registers it is contained in. + + .. automethod:: find_bit + + Similarly, you can query a circuit to see if a register has already been added to it by using + :meth:`has_register`. + + .. automethod:: has_register + + Working with compile-time parameters + ------------------------------------ + + .. seealso:: + :ref:`circuit-compile-time-parameters` + A more complete discussion of what compile-time parametrization is, and how it fits into + Qiskit's data model. + + Unlike bits, registers, and real-time typed classical data, compile-time symbolic parameters are + not manually added to a circuit. Their presence is inferred by being contained in operations + added to circuits and the global phase. An ordered list of all parameters currently in a + circuit is at :attr:`QuantumCircuit.parameters`. + + The most common operation on :class:`.Parameter` instances is to replace them in symbolic + operations with some numeric value, or another symbolic expression. This is done with + :meth:`assign_parameters`. + + .. automethod:: assign_parameters + + The circuit tracks parameters by :class:`.Parameter` instances themselves, and forbids having + multiple parameters of the same name to avoid some problems when interoperating with OpenQASM or + other external formats. You can use :meth:`has_parameter` and :meth:`get_parameter` to query + the circuit for a parameter with the given string name. + + .. automethod:: has_parameter + .. automethod:: get_parameter + + .. _circuit-real-time-methods: + + Working with real-time typed classical data + ------------------------------------------- + + .. seealso:: + :mod:`qiskit.circuit.classical` + Module-level documentation for how the variable-, expression- and type-systems work, the + objects used to represent them, and the classical operations available. + + :ref:`circuit-repr-real-time-classical` + A discussion of how real-time data fits into the entire :mod:`qiskit.circuit` data model + as a whole. + + :ref:`circuit-adding-data-objects` + The methods for adding new :class:`~.expr.Var` variables to a circuit after + initialization. + + You can retrive a :class:`~.expr.Var` instance attached to a circuit by using its variable name + using :meth:`get_var`, or check if a circuit contains a given variable with :meth:`has_var`. + + .. automethod:: get_var + .. automethod:: has_var + + There are also several iterator methods that you can use to get the full set of variables + tracked by a circuit. At least one of :meth:`iter_input_vars` and :meth:`iter_captured_vars` + will be empty, as inputs and captures are mutually exclusive. All of the iterators have + corresponding dynamic properties on :class:`QuantumCircuit` that contain their length: + :attr:`num_vars`, :attr:`num_input_vars`, :attr:`num_captured_vars` and + :attr:`num_declared_vars`. + + .. automethod:: iter_vars + .. automethod:: iter_input_vars + .. automethod:: iter_captured_vars + .. automethod:: iter_declared_vars + + + .. _circuit-adding-operations: + + Adding operations to circuits + ============================= + + You can add anything that implements the :class:`.Operation` interface to a circuit as a single + instruction, though most things you will want to add will be :class:`~.circuit.Instruction` or + :class:`~.circuit.Gate` instances. + + .. seealso:: + :ref:`circuit-operations-instructions` + The :mod:`qiskit.circuit`-level documentation on the different interfaces that Qiskit + uses to define circuit-level instructions. + + .. _circuit-append-compose: + + Methods to add general operations + --------------------------------- + + These are the base methods that handle adding any object, including user-defined ones, onto + circuits. + + =============== =============================================================================== + Method When to use it + =============== =============================================================================== + :meth:`append` Add an instruction as a single object onto a circuit. + :meth:`_append` Same as :meth:`append`, but a low-level interface that elides almost all error + checking. + :meth:`compose` Inline the instructions from one circuit onto another. + :meth:`tensor` Like :meth:`compose`, but strictly for joining circuits that act on disjoint + qubits. + =============== =============================================================================== + + :class:`QuantumCircuit` has two main ways that you will add more operations onto a circuit. + Which to use depends on whether you want to add your object as a single "instruction" + (:meth:`append`), or whether you want to join the instructions from two circuits together + (:meth:`compose`). + + A single instruction or operation appears as a single entry in the :attr:`data` of the circuit, + and as a single box when drawn in the circuit visualizers (see :meth:`draw`). A single + instruction is the "unit" that a hardware backend might be defined in terms of (see + :class:`.Target`). An :class:`~.circuit.Instruction` can come with a + :attr:`~.circuit.Instruction.definition`, which is one rule the transpiler (see + :mod:`qiskit.transpiler`) will be able to fall back on to decompose it for hardware, if needed. + An :class:`.Operation` that is not also an :class:`~.circuit.Instruction` can + only be decomposed if it has some associated high-level synthesis method registered for it (see + :mod:`qiskit.transpiler.passes.synthesis.plugin`). + + A :class:`QuantumCircuit` alone is not a single :class:`~.circuit.Instruction`; it is rather + more complicated, since it can, in general, represent a complete program with typed classical + memory inputs and outputs, and control flow. Qiskit's (and most hardware's) data model does not + yet have the concept of re-usable callable subroutines with virtual quantum operands. You can + convert simple circuits that act only on qubits with unitary operations into a :class:`.Gate` + using :meth:`to_gate`, and simple circuits acting only on qubits and clbits into a + :class:`~.circuit.Instruction` with :meth:`to_instruction`. + + When you have an :class:`.Operation`, :class:`~.circuit.Instruction`, or :class:`.Gate`, add it + to the circuit, specifying the qubit and clbit arguments with :meth:`append`. + + .. automethod:: append + + :meth:`append` does quite substantial error checking to ensure that you cannot accidentally + break the data model of :class:`QuantumCircuit`. If you are programmatically generating a + circuit from known-good data, you can elide much of this error checking by using the fast-path + appender :meth:`_append`, but at the risk that the caller is responsible for ensuring they are + passing only valid data. + + .. automethod:: _append + + In other cases, you may want to join two circuits together, applying the instructions from one + circuit onto specified qubits and clbits on another circuit. This "inlining" operation is + called :meth:`compose` in Qiskit. :meth:`compose` is, in general, more powerful than + a :meth:`to_instruction`-plus-:meth:`append` combination for joining two circuits, because it + can also link typed classical data together, and allows for circuit control-flow operations to + be joined onto another circuit. + + The downsides to :meth:`compose` are that it is a more complex operation that can involve more + rewriting of the operand, and that it necessarily must move data from one circuit object to + another. If you are building up a circuit for yourself and raw performance is a core goal, + consider passing around your base circuit and having different parts of your algorithm write + directly to the base circuit, rather than building a temporary layer circuit. + + .. automethod:: compose + + If you are trying to join two circuits that will apply to completely disjoint qubits and clbits, + :meth:`tensor` is a convenient wrapper around manually adding bit objects and calling + :meth:`compose`. + + .. automethod:: tensor + + As some rules of thumb: + + * If you have a single :class:`.Operation`, :class:`~.circuit.Instruction` or :class:`.Gate`, + you should definitely use :meth:`append` or :meth:`_append`. + * If you have a :class:`QuantumCircuit` that represents a single atomic instruction for a larger + circuit that you want to re-use, you probably want to call :meth:`to_instruction` or + :meth:`to_gate`, and then apply the result of that to the circuit using :meth:`append`. + * If you have a :class:`QuantumCircuit` that represents a larger "layer" of another circuit, or + contains typed classical variables or control flow, you should use :meth:`compose` to merge it + onto another circuit. + * :meth:`tensor` is wanted far more rarely than either :meth:`append` or :meth:`compose`. + Internally, it is mostly a wrapper around :meth:`add_bits` and :meth:`compose`. + + Some potential pitfalls to beware of: + + * Even if you re-use a custom :class:`~.circuit.Instruction` during circuit construction, the + transpiler will generally have to "unroll" each invocation of it to its inner decomposition + before beginning work on it. This should not prevent you from using the + :meth:`to_instruction`-plus-:meth:`append` pattern, as the transpiler will improve in this + regard over time. + * :meth:`compose` will, by default, produce a new circuit for backwards compatibility. This is + more expensive, and not usually what you want, so you should set ``inplace=True``. + * Both :meth:`append` and :meth:`compose` (but not :meth:`_append`) have a ``copy`` keyword + argument that defaults to ``True``. In these cases, the incoming :class:`.Operation` + instances will be copied if Qiskit detects that the objects have mutability about them (such + as taking gate parameters). If you are sure that you will not re-use the objects again in + other places, you should set ``copy=False`` to prevent this copying, which can be a + substantial speed-up for large objects. + + Methods to add standard instructions + ------------------------------------ + + The :class:`QuantumCircuit` class has helper methods to add many of the Qiskit standard-library + instructions and gates onto a circuit. These are generally equivalent to manually constructing + an instance of the relevent :mod:`qiskit.circuit.library` object, then passing that to + :meth:`append` with the remaining arguments placed into the ``qargs`` and ``cargs`` fields as + appropriate. + + The following methods apply special non-unitary :class:`~.circuit.Instruction` operations to the + circuit: + + =============================== ==================================================== + :class:`QuantumCircuit` method :mod:`qiskit.circuit` :class:`~.circuit.Instruction` + =============================== ==================================================== + :meth:`barrier` :class:`Barrier` + :meth:`delay` :class:`Delay` + :meth:`initialize` :class:`~library.Initialize` + :meth:`measure` :class:`Measure` + :meth:`reset` :class:`Reset` + :meth:`store` :class:`Store` + =============================== ==================================================== + + These methods apply uncontrolled unitary :class:`.Gate` instances to the circuit: + + =============================== ============================================ + :class:`QuantumCircuit` method :mod:`qiskit.circuit.library` :class:`.Gate` + =============================== ============================================ + :meth:`dcx` :class:`~library.DCXGate` + :meth:`ecr` :class:`~library.ECRGate` + :meth:`h` :class:`~library.HGate` + :meth:`id` :class:`~library.IGate` + :meth:`iswap` :class:`~library.iSwapGate` + :meth:`ms` :class:`~library.MSGate` + :meth:`p` :class:`~library.PhaseGate` + :meth:`pauli` :class:`~library.PauliGate` + :meth:`prepare_state` :class:`~library.StatePreparation` + :meth:`r` :class:`~library.RGate` + :meth:`rcccx` :class:`~library.RC3XGate` + :meth:`rccx` :class:`~library.RCCXGate` + :meth:`rv` :class:`~library.RVGate` + :meth:`rx` :class:`~library.RXGate` + :meth:`rxx` :class:`~library.RXXGate` + :meth:`ry` :class:`~library.RYGate` + :meth:`ryy` :class:`~library.RYYGate` + :meth:`rz` :class:`~library.RZGate` + :meth:`rzx` :class:`~library.RZXGate` + :meth:`rzz` :class:`~library.RZZGate` + :meth:`s` :class:`~library.SGate` + :meth:`sdg` :class:`~library.SdgGate` + :meth:`swap` :class:`~library.SwapGate` + :meth:`sx` :class:`~library.SXGate` + :meth:`sxdg` :class:`~library.SXdgGate` + :meth:`t` :class:`~library.TGate` + :meth:`tdg` :class:`~library.TdgGate` + :meth:`u` :class:`~library.UGate` + :meth:`unitary` :class:`~library.UnitaryGate` + :meth:`x` :class:`~library.XGate` + :meth:`y` :class:`~library.YGate` + :meth:`z` :class:`~library.ZGate` + =============================== ============================================ + + The following methods apply :class:`Gate` instances that are also controlled gates, so are + direct subclasses of :class:`ControlledGate`: + + =============================== ====================================================== + :class:`QuantumCircuit` method :mod:`qiskit.circuit.library` :class:`.ControlledGate` + =============================== ====================================================== + :meth:`ccx` :class:`~library.CCXGate` + :meth:`ccz` :class:`~library.CCZGate` + :meth:`ch` :class:`~library.CHGate` + :meth:`cp` :class:`~library.CPhaseGate` + :meth:`crx` :class:`~library.CRXGate` + :meth:`cry` :class:`~library.CRYGate` + :meth:`crz` :class:`~library.CRZGate` + :meth:`cs` :class:`~library.CSGate` + :meth:`csdg` :class:`~library.CSdgGate` + :meth:`cswap` :class:`~library.CSwapGate` + :meth:`csx` :class:`~library.CSXGate` + :meth:`cu` :class:`~library.CUGate` + :meth:`cx` :class:`~library.CXGate` + :meth:`cy` :class:`~library.CYGate` + :meth:`cz` :class:`~library.CZGate` + =============================== ====================================================== + + Finally, these methods apply particular generalized multiply controlled gates to the circuit, + often with eager syntheses. They are listed in terms of the *base* gate they are controlling, + since their exact output is often a synthesised version of a gate. + + =============================== ================================================= + :class:`QuantumCircuit` method Base :mod:`qiskit.circuit.library` :class:`.Gate` + =============================== ================================================= + :meth:`mcp` :class:`~library.PhaseGate` + :meth:`mcrx` :class:`~library.RXGate` + :meth:`mcry` :class:`~library.RYGate` + :meth:`mcrz` :class:`~library.RZGate` + :meth:`mcx` :class:`~library.XGate` + =============================== ================================================= + + The rest of this section is the API listing of all the individual methods; the tables above are + summaries whose links will jump you to the correct place. + + .. automethod:: barrier + .. automethod:: ccx + .. automethod:: ccz + .. automethod:: ch + .. automethod:: cp + .. automethod:: crx + .. automethod:: cry + .. automethod:: crz + .. automethod:: cs + .. automethod:: csdg + .. automethod:: cswap + .. automethod:: csx + .. automethod:: cu + .. automethod:: cx + .. automethod:: cy + .. automethod:: cz + .. automethod:: dcx + .. automethod:: delay + .. automethod:: ecr + .. automethod:: h + .. automethod:: id + .. automethod:: initialize + .. automethod:: iswap + .. automethod:: mcp + .. automethod:: mcrx + .. automethod:: mcry + .. automethod:: mcrz + .. automethod:: mcx + .. automethod:: measure + .. automethod:: ms + .. automethod:: p + .. automethod:: pauli + .. automethod:: prepare_state + .. automethod:: r + .. automethod:: rcccx + .. automethod:: rccx + .. automethod:: reset + .. automethod:: rv + .. automethod:: rx + .. automethod:: rxx + .. automethod:: ry + .. automethod:: ryy + .. automethod:: rz + .. automethod:: rzx + .. automethod:: rzz + .. automethod:: s + .. automethod:: sdg + .. automethod:: store + .. automethod:: swap + .. automethod:: sx + .. automethod:: sxdg + .. automethod:: t + .. automethod:: tdg + .. automethod:: u + .. automethod:: unitary + .. automethod:: x + .. automethod:: y + .. automethod:: z + + + .. _circuit-control-flow-methods: + + Adding control flow to circuits + ------------------------------- + + .. seealso:: + :ref:`circuit-control-flow-repr` + + Discussion of how control-flow operations are represented in the whole :mod:`qiskit.circuit` + context. + + ============================== ================================================================ + :class:`QuantumCircuit` method Control-flow instruction + ============================== ================================================================ + :meth:`if_test` :class:`.IfElseOp` with only a ``True`` body. + :meth:`if_else` :class:`.IfElseOp` with both ``True`` and ``False`` bodies. + :meth:`while_loop` :class:`.WhileLoopOp`. + :meth:`switch` :class:`.SwitchCaseOp`. + :meth:`for_loop` :class:`.ForLoopOp`. + :meth:`break_loop` :class:`.BreakLoopOp`. + :meth:`continue_loop` :class:`.ContinueLoopOp`. + ============================== ================================================================ + + :class:`QuantumCircuit` has corresponding methods for all of the control-flow operations that + are supported by Qiskit. These have two forms for calling them. The first is a very + straightfowards convenience wrapper that takes in the block bodies of the instructions as + :class:`QuantumCircuit` arguments, and simply constructs and appends the corresponding + :class:`.ControlFlowOp`. + + The second form, which we strongly recommend you use for constructing control flow, is called + *the builder interface*. Here, the methods take only the real-time discriminant of the + operation, and return `context managers + `__ that you enter using + ``with``. You can then use regular :class:`QuantumCircuit` methods within those blocks to build + up the control-flow bodies, and Qiskit will automatically track which of the data resources are + needed for the inner blocks, building the complete :class:`.ControlFlowOp` as you leave the + ``with`` statement. It is far simpler and less error-prone to build control flow + programmatically this way. + + .. + TODO: expand the examples of the builder interface. + + .. automethod:: break_loop + .. automethod:: continue_loop + .. automethod:: for_loop + .. automethod:: if_else + .. automethod:: if_test + .. automethod:: switch + .. automethod:: while_loop + + + Converting circuits to single objects + ------------------------------------- + + As discussed in :ref:`circuit-append-compose`, you can convert a circuit to either an + :class:`~.circuit.Instruction` or a :class:`.Gate` using two helper methods. + + .. automethod:: to_instruction + .. automethod:: to_gate + + + Helper mutation methods + ----------------------- + + There are two higher-level methods on :class:`QuantumCircuit` for appending measurements to the + end of a circuit. Note that by default, these also add an extra register. + + .. automethod:: measure_active + .. automethod:: measure_all + + There are two "subtractive" methods on :class:`QuantumCircuit` as well. This is not a use-case + that :class:`QuantumCircuit` is designed for; typically you should just look to use + :meth:`copy_empty_like` in place of :meth:`clear`, and run :meth:`remove_final_measurements` as + its transpiler-pass form :class:`.RemoveFinalMeasurements`. + + .. automethod:: clear + .. automethod:: remove_final_measurements + + .. _circuit-calibrations: + + Manual calibration of instructions + ---------------------------------- + + :class:`QuantumCircuit` can store :attr:`calibrations` of instructions that define the pulses + used to run them on one particular hardware backend. You can + + .. automethod:: add_calibration + .. automethod:: has_calibration_for + + + Circuit properties + ================== + + Simple circuit metrics + ---------------------- + + When constructing quantum circuits, there are several properties that help quantify + the "size" of the circuits, and their ability to be run on a noisy quantum device. + Some of these, like number of qubits, are straightforward to understand, while others + like depth and number of tensor components require a bit more explanation. Here we will + explain all of these properties, and, in preparation for understanding how circuits change + when run on actual devices, highlight the conditions under which they change. + + Consider the following circuit: + + .. plot:: + :include-source: + + from qiskit import QuantumCircuit + qc = QuantumCircuit(12) + for idx in range(5): + qc.h(idx) + qc.cx(idx, idx+5) + + qc.cx(1, 7) + qc.x(8) + qc.cx(1, 9) + qc.x(7) + qc.cx(1, 11) + qc.swap(6, 11) + qc.swap(6, 9) + qc.swap(6, 10) + qc.x(6) + qc.draw('mpl') + + From the plot, it is easy to see that this circuit has 12 qubits, and a collection of + Hadamard, CNOT, X, and SWAP gates. But how to quantify this programmatically? Because we + can do single-qubit gates on all the qubits simultaneously, the number of qubits in this + circuit is equal to the :meth:`width` of the circuit:: + + assert qc.width() == 12 + + We can also just get the number of qubits directly using :attr:`num_qubits`:: + + assert qc.num_qubits == 12 + + .. important:: + + For a quantum circuit composed from just qubits, the circuit width is equal + to the number of qubits. This is the definition used in quantum computing. However, + for more complicated circuits with classical registers, and classically controlled gates, + this equivalence breaks down. As such, from now on we will not refer to the number of + qubits in a quantum circuit as the width. + + It is also straightforward to get the number and type of the gates in a circuit using + :meth:`count_ops`:: + + qc.count_ops() - Examples: + .. parsed-literal:: - Construct a simple Bell state circuit. + OrderedDict([('cx', 8), ('h', 5), ('x', 3), ('swap', 3)]) - .. plot:: - :include-source: + We can also get just the raw count of operations by computing the circuits + :meth:`size`:: - from qiskit import QuantumCircuit + assert qc.size() == 19 - qc = QuantumCircuit(2, 2) - qc.h(0) - qc.cx(0, 1) - qc.measure([0, 1], [0, 1]) - qc.draw('mpl') + A particularly important circuit property is known as the circuit :meth:`depth`. The depth + of a quantum circuit is a measure of how many "layers" of quantum gates, executed in + parallel, it takes to complete the computation defined by the circuit. Because quantum + gates take time to implement, the depth of a circuit roughly corresponds to the amount of + time it takes the quantum computer to execute the circuit. Thus, the depth of a circuit + is one important quantity used to measure if a quantum circuit can be run on a device. - Construct a 5-qubit GHZ circuit. + The depth of a quantum circuit has a mathematical definition as the longest path in a + directed acyclic graph (DAG). However, such a definition is a bit hard to grasp, even for + experts. Fortunately, the depth of a circuit can be easily understood by anyone familiar + with playing `Tetris `_. Lets see how to compute this + graphically: - .. code-block:: + .. image:: /source_images/depth.gif - from qiskit import QuantumCircuit + We can verify our graphical result using :meth:`QuantumCircuit.depth`:: - qc = QuantumCircuit(5) - qc.h(0) - qc.cx(0, range(1, 5)) - qc.measure_all() + assert qc.depth() == 9 - Construct a 4-qubit Bernstein-Vazirani circuit using registers. + .. automethod:: count_ops + .. automethod:: depth + .. automethod:: get_instructions + .. automethod:: num_connected_components + .. automethod:: num_nonlocal_gates + .. automethod:: num_tensor_factors + .. automethod:: num_unitary_factors + .. automethod:: size + .. automethod:: width - .. plot:: - :include-source: + Accessing scheduling information + -------------------------------- - from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit + If a :class:`QuantumCircuit` has been scheduled as part of a transpilation pipeline, the timing + information for individual qubits can be accessed. The whole-circuit timing information is + available through the :attr:`duration`, :attr:`unit` and :attr:`op_start_times` attributes. + + .. automethod:: qubit_duration + .. automethod:: qubit_start_time + .. automethod:: qubit_stop_time + + Instruction-like methods + ======================== - qr = QuantumRegister(3, 'q') - anc = QuantumRegister(1, 'ancilla') - cr = ClassicalRegister(3, 'c') - qc = QuantumCircuit(qr, anc, cr) + .. + These methods really shouldn't be on `QuantumCircuit` at all. They're generally more + appropriate as `Instruction` or `Gate` methods. `reverse_ops` shouldn't be a method _full + stop_---it was copying a `DAGCircuit` method from an implementation detail of the original + `SabreLayout` pass in Qiskit. + + :class:`QuantumCircuit` also contains a small number of methods that are very + :class:`~.circuit.Instruction`-like in detail. You may well find better integration and more + API support if you first convert your circuit to an :class:`~.circuit.Instruction` + (:meth:`to_instruction`) or :class:`.Gate` (:meth:`to_gate`) as appropriate, then call the + corresponding method. - qc.x(anc[0]) - qc.h(anc[0]) - qc.h(qr[0:3]) - qc.cx(qr[0:3], anc[0]) - qc.h(qr[0:3]) - qc.barrier(qr) - qc.measure(qr, cr) + .. automethod:: control + .. automethod:: inverse + .. automethod:: power + .. automethod:: repeat + .. automethod:: reverse_ops - qc.draw('mpl') + Visualization + ============= + + Qiskit includes some drawing tools to give you a quick feel for what your circuit looks like. + This tooling is primarily targeted at producing either a `Matplotlib + `__- or text-based drawing. There is also a lesser-featured LaTeX + backend for drawing, but this is only for simple circuits, and is not as actively maintained. + + .. seealso:: + :mod:`qiskit.visualization` + The primary documentation for all of Qiskit's visualization tooling. + + .. automethod:: draw + + In addition to the core :meth:`draw` driver, there are two visualization-related helper methods, + which are mostly useful for quickly unwrapping some inner instructions or reversing the + :ref:`qubit-labelling conventions ` in the drawing. For more general + mutation, including basis-gate rewriting, you should use the transpiler + (:mod:`qiskit.transpiler`). + + .. automethod:: decompose + .. automethod:: reverse_bits + + Internal utilities + ================== + + These functions are not intended for public use, but were accidentally left documented in the + public API during the 1.0 release. They will be removed in Qiskit 2.0, but will be supported + until then. + + .. automethod:: cast + .. automethod:: cbit_argument_conversion + .. automethod:: cls_instances + .. automethod:: cls_prefix + .. automethod:: qbit_argument_conversion """ instances = 0 @@ -228,6 +993,69 @@ def __init__( captures: Iterable[expr.Var] = (), declarations: Mapping[expr.Var, expr.Expr] | Iterable[Tuple[expr.Var, expr.Expr]] = (), ): + """ + Default constructor of :class:`QuantumCircuit`. + + .. + `QuantumCirucit` documents its `__init__` method explicitly, unlike most classes where + it's implicitly appended to the class-level documentation, just because the class is so + huge and has a lot of introductory material to its class docstring. + + Args: + regs: The registers to be included in the circuit. + + * If a list of :class:`~.Register` objects, represents the :class:`.QuantumRegister` + and/or :class:`.ClassicalRegister` objects to include in the circuit. + + For example: + + * ``QuantumCircuit(QuantumRegister(4))`` + * ``QuantumCircuit(QuantumRegister(4), ClassicalRegister(3))`` + * ``QuantumCircuit(QuantumRegister(4, 'qr0'), QuantumRegister(2, 'qr1'))`` + + * If a list of ``int``, the amount of qubits and/or classical bits to include in + the circuit. It can either be a single int for just the number of quantum bits, + or 2 ints for the number of quantum bits and classical bits, respectively. + + For example: + + * ``QuantumCircuit(4) # A QuantumCircuit with 4 qubits`` + * ``QuantumCircuit(4, 3) # A QuantumCircuit with 4 qubits and 3 classical bits`` + + * If a list of python lists containing :class:`.Bit` objects, a collection of + :class:`.Bit` s to be added to the circuit. + + name: the name of the quantum circuit. If not set, an automatically generated string + will be assigned. + global_phase: The global phase of the circuit in radians. + metadata: Arbitrary key value metadata to associate with the circuit. This gets + stored as free-form data in a dict in the + :attr:`~qiskit.circuit.QuantumCircuit.metadata` attribute. It will not be directly + used in the circuit. + inputs: any variables to declare as ``input`` runtime variables for this circuit. These + should already be existing :class:`.expr.Var` nodes that you build from somewhere + else; if you need to create the inputs as well, use + :meth:`QuantumCircuit.add_input`. The variables given in this argument will be + passed directly to :meth:`add_input`. A circuit cannot have both ``inputs`` and + ``captures``. + captures: any variables that that this circuit scope should capture from a containing + scope. The variables given here will be passed directly to :meth:`add_capture`. A + circuit cannot have both ``inputs`` and ``captures``. + declarations: any variables that this circuit should declare and initialize immediately. + You can order this input so that later declarations depend on earlier ones + (including inputs or captures). If you need to depend on values that will be + computed later at runtime, use :meth:`add_var` at an appropriate point in the + circuit execution. + + This argument is intended for convenient circuit initialization when you already + have a set of created variables. The variables used here will be directly passed to + :meth:`add_var`, which you can use directly if this is the first time you are + creating the variable. + + Raises: + CircuitError: if the circuit name, if given, is not valid. + CircuitError: if both ``inputs`` and ``captures`` are given. + """ if any(not isinstance(reg, (list, QuantumRegister, ClassicalRegister)) for reg in regs): # check if inputs are integers, but also allow e.g. 2.0 @@ -244,6 +1072,8 @@ def __init__( regs = tuple(int(reg) for reg in regs) # cast to int self._base_name = None + self.name: str + """A human-readable name for the circuit.""" if name is None: self._base_name = self.cls_prefix() self._name_update() @@ -273,7 +1103,11 @@ def __init__( ] = [] self.qregs: list[QuantumRegister] = [] + """A list of the :class:`QuantumRegister`\\ s in this circuit. You should not mutate + this.""" self.cregs: list[ClassicalRegister] = [] + """A list of the :class:`ClassicalRegister`\\ s in this circuit. You should not mutate + this.""" # Dict mapping Qubit or Clbit instances to tuple comprised of 0) the # corresponding index in circuit.{qubits,clbits} and 1) a list of @@ -314,9 +1148,16 @@ def __init__( for var, initial in declarations: self.add_var(var, initial) - self.duration = None + self.duration: int | float | None = None + """The total duration of the circuit, set by a scheduling transpiler pass. Its unit is + specified by :attr:`unit`.""" self.unit = "dt" + """The unit that :attr:`duration` is specified in.""" self.metadata = {} if metadata is None else metadata + """Arbitrary user-defined metadata for the circuit. + + Qiskit will not examine the content of this mapping, but it will pass it through the + transpiler and reattach it to the output, so you can track your own metadata.""" @staticmethod def from_instructions( @@ -333,7 +1174,7 @@ def from_instructions( global_phase: ParameterValueType = 0, metadata: dict | None = None, ) -> "QuantumCircuit": - """Construct a circuit from an iterable of CircuitInstructions. + """Construct a circuit from an iterable of :class:`.CircuitInstruction`\\ s. Args: instructions: The instructions to add to the circuit. @@ -390,7 +1231,7 @@ def layout(self) -> Optional[TranspileLayout]: @property def data(self) -> QuantumCircuitData: - """Return the circuit data (instructions and context). + """The circuit data (instructions and context). Returns: QuantumCircuitData: a list-like object containing the :class:`.CircuitInstruction`\\ s @@ -884,9 +1725,13 @@ def compose( var_remap: Mapping[str | expr.Var, str | expr.Var] | None = None, inline_captures: bool = False, ) -> Optional["QuantumCircuit"]: - """Compose circuit with ``other`` circuit or instruction, optionally permuting wires. + """Apply the instructions from one circuit onto specified qubits and/or clbits on another. + + .. note:: - ``other`` can be narrower or of equal width to ``self``. + By default, this creates a new circuit object, leaving ``self`` untouched. For most + uses of this function, it is far more efficient to set ``inplace=True`` and modify the + base circuit in-place. When dealing with realtime variables (:class:`.expr.Var` instances), there are two principal strategies for using :meth:`compose`: @@ -915,8 +1760,6 @@ def compose( front (bool): If True, front composition will be performed. This is not possible within control-flow builder context managers. inplace (bool): If True, modify the object. Otherwise return composed circuit. - wrap (bool): If True, wraps the other circuit into a gate (or instruction, depending on - whether it contains only unitary instructions) before composing it onto self. copy (bool): If ``True`` (the default), then the input is treated as shared, and any contained instructions will be copied, if they might need to be mutated in the future. You can set this to ``False`` if the input should be considered owned by @@ -941,6 +1784,11 @@ def compose( If this is ``False`` (the default), then all variables in ``other`` will be required to be distinct from those in ``self``, and new declarations will be made for them. + wrap (bool): If True, wraps the other circuit into a gate (or instruction, depending on + whether it contains only unitary instructions) before composing it onto self. + Rather than using this option, it is almost always better to manually control this + yourself by using :meth:`to_instruction` or :meth:`to_gate`, and then call + :meth:`append`. Returns: QuantumCircuit: the composed circuit (returns None if inplace==True). @@ -1294,23 +2142,20 @@ def tensor(self, other: "QuantumCircuit", inplace: bool = False) -> Optional["Qu @property def qubits(self) -> list[Qubit]: - """ - Returns a list of quantum bits in the order that the registers were added. - """ + """A list of :class:`Qubit`\\ s in the order that they were added. You should not mutate + this.""" return self._data.qubits @property def clbits(self) -> list[Clbit]: - """ - Returns a list of classical bits in the order that the registers were added. - """ + """A list of :class:`Clbit`\\ s in the order that they were added. You should not mutate + this.""" return self._data.clbits @property def ancillas(self) -> list[AncillaQubit]: - """ - Returns a list of ancilla bits in the order that the registers were added. - """ + """A list of :class:`AncillaQubit`\\ s in the order that they were added. You should not + mutate this.""" return self._ancillas @property @@ -1557,33 +2402,31 @@ def append( # Preferred new style. @typing.overload - def _append( - self, instruction: CircuitInstruction, _qargs: None = None, _cargs: None = None - ) -> CircuitInstruction: ... + def _append(self, instruction: CircuitInstruction) -> CircuitInstruction: ... # To-be-deprecated old style. @typing.overload def _append( self, - operation: Operation, + instruction: Operation, qargs: Sequence[Qubit], cargs: Sequence[Clbit], ) -> Operation: ... - def _append( - self, - instruction: CircuitInstruction | Instruction, - qargs: Sequence[Qubit] | None = None, - cargs: Sequence[Clbit] | None = None, - ): + def _append(self, instruction, qargs=(), cargs=()): """Append an instruction to the end of the circuit, modifying the circuit in place. .. warning:: This is an internal fast-path function, and it is the responsibility of the caller to ensure that all the arguments are valid; there is no error checking here. In - particular, all the qubits and clbits must already exist in the circuit and there can be - no duplicates in the list. + particular: + + * all the qubits and clbits must already exist in the circuit and there can be no + duplicates in the list. + * any control-flow operations or classically conditioned instructions must act only on + variables present in the circuit. + * the circuit must not be within a control-flow builder context. .. note:: @@ -1596,12 +2439,18 @@ def _append( constructs of the control-flow builder interface. Args: - instruction: Operation instance to append - qargs: Qubits to attach the instruction to. - cargs: Clbits to attach the instruction to. + instruction: A complete well-formed :class:`.CircuitInstruction` of the operation and + its context to be added. + + In the legacy compatibility form, this can be a bare :class:`.Operation`, in which + case ``qargs`` and ``cargs`` must be explicitly given. + qargs: Legacy argument for qubits to attach the bare :class:`.Operation` to. Ignored if + the first argument is in the preferential :class:`.CircuitInstruction` form. + cargs: Legacy argument for clbits to attach the bare :class:`.Operation` to. Ignored if + the first argument is in the preferential :class:`.CircuitInstruction` form. Returns: - Operation: a handle to the instruction that was just added + CircuitInstruction: a handle to the instruction that was just added. :meta public: """ @@ -2114,24 +2963,52 @@ def add_bits(self, bits: Iterable[Bit]) -> None: def find_bit(self, bit: Bit) -> BitLocations: """Find locations in the circuit which can be used to reference a given :obj:`~Bit`. + In particular, this function can find the integer index of a qubit, which corresponds to its + hardware index for a transpiled circuit. + + .. note:: + The circuit index of a :class:`.AncillaQubit` will be its index in :attr:`qubits`, not + :attr:`ancillas`. + Args: bit (Bit): The bit to locate. Returns: namedtuple(int, List[Tuple(Register, int)]): A 2-tuple. The first element (``index``) - contains the index at which the ``Bit`` can be found (in either - :obj:`~QuantumCircuit.qubits`, :obj:`~QuantumCircuit.clbits`, depending on its - type). The second element (``registers``) is a list of ``(register, index)`` - pairs with an entry for each :obj:`~Register` in the circuit which contains the - :obj:`~Bit` (and the index in the :obj:`~Register` at which it can be found). - - Notes: - The circuit index of an :obj:`~AncillaQubit` will be its index in - :obj:`~QuantumCircuit.qubits`, not :obj:`~QuantumCircuit.ancillas`. + contains the index at which the ``Bit`` can be found (in either + :obj:`~QuantumCircuit.qubits`, :obj:`~QuantumCircuit.clbits`, depending on its + type). The second element (``registers``) is a list of ``(register, index)`` + pairs with an entry for each :obj:`~Register` in the circuit which contains the + :obj:`~Bit` (and the index in the :obj:`~Register` at which it can be found). Raises: CircuitError: If the supplied :obj:`~Bit` was of an unknown type. CircuitError: If the supplied :obj:`~Bit` could not be found on the circuit. + + Examples: + Loop through a circuit, getting the qubit and clbit indices of each operation:: + + from qiskit.circuit import QuantumCircuit, Qubit + + qc = QuantumCircuit(3, 3) + qc.h(0) + qc.cx(0, 1) + qc.cx(1, 2) + qc.measure([0, 1, 2], [0, 1, 2]) + + # The `.qubits` and `.clbits` fields are not integers. + assert isinstance(qc.data[0].qubits[0], Qubit) + # ... but we can use `find_bit` to retrieve them. + assert qc.find_bit(qc.data[0].qubits[0]).index == 0 + + simple = [ + ( + instruction.operation.name, + [qc.find_bit(bit).index for bit in instruction.qubits], + [qc.find_bit(bit).index for bit in instruction.clbits], + ) + for instruction in qc.data + ] """ try: @@ -2157,18 +3034,22 @@ def to_instruction( parameter_map: dict[Parameter, ParameterValueType] | None = None, label: str | None = None, ) -> Instruction: - """Create an Instruction out of this circuit. + """Create an :class:`~.circuit.Instruction` out of this circuit. + + .. seealso:: + :func:`circuit_to_instruction` + The underlying driver of this method. Args: - parameter_map(dict): For parameterized circuits, a mapping from + parameter_map: For parameterized circuits, a mapping from parameters in the circuit to parameters to be used in the instruction. If None, existing circuit parameters will also parameterize the instruction. - label (str): Optional gate label. + label: Optional gate label. Returns: - qiskit.circuit.Instruction: a composite instruction encapsulating this circuit - (can be decomposed back) + qiskit.circuit.Instruction: a composite instruction encapsulating this circuit (can be + decomposed back). """ from qiskit.converters.circuit_to_instruction import circuit_to_instruction @@ -2179,18 +3060,21 @@ def to_gate( parameter_map: dict[Parameter, ParameterValueType] | None = None, label: str | None = None, ) -> Gate: - """Create a Gate out of this circuit. + """Create a :class:`.Gate` out of this circuit. The circuit must act only qubits and + contain only unitary operations. + + .. seealso:: + :func:`circuit_to_gate` + The underlying driver of this method. Args: - parameter_map(dict): For parameterized circuits, a mapping from - parameters in the circuit to parameters to be used in the - gate. If None, existing circuit parameters will also - parameterize the gate. - label (str): Optional gate label. + parameter_map: For parameterized circuits, a mapping from parameters in the circuit to + parameters to be used in the gate. If ``None``, existing circuit parameters will + also parameterize the gate. + label : Optional gate label. Returns: - Gate: a composite gate encapsulating this circuit - (can be decomposed back) + Gate: a composite gate encapsulating this circuit (can be decomposed back). """ from qiskit.converters.circuit_to_gate import circuit_to_gate @@ -2417,25 +3301,36 @@ def size( def depth( self, - filter_function: Callable[..., int] = lambda x: not getattr( + filter_function: Callable[[CircuitInstruction], bool] = lambda x: not getattr( x.operation, "_directive", False ), ) -> int: """Return circuit depth (i.e., length of critical path). Args: - filter_function (callable): A function to filter instructions. - Should take as input a tuple of (Instruction, list(Qubit), list(Clbit)). - Instructions for which the function returns False are ignored in the - computation of the circuit depth. - By default filters out "directives", such as barrier or snapshot. + filter_function: A function to decide which instructions count to increase depth. + Should take as a single positional input a :class:`CircuitInstruction`. + Instructions for which the function returns ``False`` are ignored in the + computation of the circuit depth. By default filters out "directives", such as + :class:`.Barrier`. Returns: int: Depth of circuit. - Notes: - The circuit depth and the DAG depth need not be the - same. + Examples: + Simple calculation of total circuit depth:: + + from qiskit.circuit import QuantumCircuit + qc = QuantumCircuit(4) + qc.h(0) + qc.cx(0, 1) + qc.h(2) + qc.cx(2, 3) + assert qc.depth() == 2 + + Modifying the previous example to only calculate the depth of multi-qubit gates:: + + assert qc.depth(lambda instr: len(instr.qubits) > 1) == 1 """ # Assign each bit in the circuit a unique integer # to index into op_stack. @@ -2773,6 +3668,11 @@ def clear(self) -> None: """Clear all instructions in self. Clearing the circuits will keep the metadata and calibrations. + + .. seealso:: + :meth:`copy_empty_like` + A method to produce a new circuit with no instructions and all the same tracking of + quantum and classical typed data, but without mutating the original circuit. """ self._data.clear() self._parameter_table.clear() @@ -3007,6 +3907,28 @@ def remove_final_measurements(self, inplace: bool = True) -> Optional["QuantumCi Measurements and barriers are considered final if they are followed by no other operations (aside from other measurements or barriers.) + .. note:: + This method has rather complex behavior, particularly around the removal of newly idle + classical bits and registers. It is much more efficient to avoid adding unnecessary + classical data in the first place, rather than trying to remove it later. + + .. seealso:: + :class:`.RemoveFinalMeasurements` + A transpiler pass that removes final measurements and barriers. This does not + remove the classical data. If this is your goal, you can call that with:: + + from qiskit.circuit import QuantumCircuit + from qiskit.transpiler.passes import RemoveFinalMeasurements + + qc = QuantumCircuit(2, 2) + qc.h(0) + qc.cx(0, 1) + qc.barrier() + qc.measure([0, 1], [0, 1]) + + pass_ = RemoveFinalMeasurements() + just_bell = pass_(qc) + Args: inplace (bool): All measurements removed inplace or return new circuit. @@ -3110,7 +4032,7 @@ def from_qasm_str(qasm_str: str) -> "QuantumCircuit": @property def global_phase(self) -> ParameterValueType: - """Return the global phase of the current circuit scope in radians.""" + """The global phase of the current circuit scope in radians.""" if self._control_flow_scopes: return self._control_flow_scopes[-1].global_phase return self._global_phase @@ -5206,15 +6128,7 @@ def for_loop( ) @typing.overload - def if_test( - self, - condition: tuple[ClassicalRegister | Clbit, int], - true_body: None, - qubits: None, - clbits: None, - *, - label: str | None, - ) -> IfContext: ... + def if_test(self, condition: tuple[ClassicalRegister | Clbit, int]) -> IfContext: ... @typing.overload def if_test(