diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 4e31c75fbd48..fddd907195fc 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -1,23 +1,13 @@ .. _release-notes: -============= -Release Notes -============= - -This page contains the release notes for Qiskit, starting from Qiskit 0.45, the first time that Qiskit and Qiskit Terra had the same versions. +============================== +Qiskit |version| release notes +============================== .. - These release notes get converted into Markdown files via the infrastructure at https://github.com/Qiskit/documentation, which then gets deployed to https://docs.quantum.ibm.com/api/qiskit/release-notes. Changes to these release notes will update those release notes the next time the API docs are generated. - - To change release notes prior to Qiskit 0.45, update the Qiskit/documentation repository directly. + These release notes get converted into Markdown files via the infrastructure at https://github.com/Qiskit/documentation, which then gets deployed to https://docs.quantum.ibm.com/api/qiskit/release-notes. Changes to these release notes will update those release notes the next time the API docs are generated for this version. -.. release-notes:: - :earliest-version: 0.45.0rc1 - -.. release-notes:: - :earliest-version: 0.45.0 - :branch: stable/0.46 + `:earliest-version:` should be set to the rc1 release for the current minor release series. For example, the stable/1.1 branch should set it to 1.1.0rc1. If on `main`, set to the prior minor version's rc1, like `1.0.0rc1`. .. release-notes:: - :earliest-version: 0.45.0 - :branch: stable/0.45 + :earliest-version: 1.0.0rc1 diff --git a/qiskit/circuit/__init__.py b/qiskit/circuit/__init__.py index a8aec764215c..2e8603af9f7d 100644 --- a/qiskit/circuit/__init__.py +++ b/qiskit/circuit/__init__.py @@ -10,38 +10,64 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -======================================== -Quantum Circuits (:mod:`qiskit.circuit`) -======================================== +r""" +============================================= +Quantum circuit model (:mod:`qiskit.circuit`) +============================================= .. currentmodule:: qiskit.circuit -Overview -======== +The fundamental element of quantum computing is the *quantum circuit*. This is a computational +routine that can be run, one shot at a time, on a quantum processing unit (QPU). A circuit will act +on a predefined amount of quantum data (in Qiskit, we only directly support qubits) with unitary +operations (gates), measurements and resets. In addition, a quantum circuit can contain operations +on classical data, including real-time computations and control-flow constructs, which are executed +by the controllers of the QPU. + +.. note:: + + You may wish to skip the introductory material and jump directly to: + + * :ref:`the API overview of the whole circuit module ` + * :ref:`the detailed discussion about how circuits are represented ` + * the core :class:`QuantumCircuit` class for how to build and query circuits + * :ref:`information on construction custom instructions ` + * :ref:`ways to work with circuit-level objects ` + * :ref:`discussion of Qiskit conventions for circuits, matrices and state labelling + ` + +Circuits are at a low level of abstraction when building up quantum programs. They are the +construct that is used to build up to higher levels of abstraction, such as the :ref:`primitives of +quantum computation `, which accumulate data from many shots of quantum-circuit +execution, along with advanced error-mitigation techniques and measurement optimizations, into +well-typed classical data and error statistics. + +In Qiskit, circuits can be defined in one of two regimes: -The fundamental element of quantum computing is the **quantum circuit**. -A quantum circuit is a computational routine consisting of coherent quantum -operations on quantum data, such as qubits. It is an ordered sequence of quantum -gates, measurements and resets, which may be conditioned on real-time classical -computation. A set of quantum gates is said to be universal if any unitary -transformation of the quantum data can be efficiently approximated arbitrarily well -as a sequence of gates in the set. Any quantum program can be represented by a -sequence of quantum circuits and classical near-time computation. +* an *abstract* circuit, which is defined in terms of *virtual qubits* and arbitrary high-level + operations, like encapsulated algorithms and user-defined gates. -In Qiskit, this core element is represented by the :class:`QuantumCircuit` class. -Below is an example of a quantum circuit that makes a three-qubit GHZ state +* a *physical* circuit, which is defined in terms of the *hardware qubits* of one particular + backend, and contains only operations that this backend natively supports. You might also see + this concept referred to as an *ISA circuit*. + +You convert from an abstract circuit to a physical circuit by using :ref:`Qiskit's transpilation +package `, of which the top-level access point is :func:`.transpile`. + +In Qiskit, a quantum circuit is represented by the :class:`QuantumCircuit` class. Below is an +example of a quantum circuit that makes a three-qubit Greenberger–Horne–Zeilinger (GHZ) state defined as: .. math:: - |\\psi\\rangle = \\left(|000\\rangle+|111\\rangle\\right)/\\sqrt{2} + |\psi\rangle = \left( |000\rangle + |111\rangle \right) / \sqrt{2} .. plot:: :include-source: from qiskit import QuantumCircuit + # Create a circuit with a register of three qubits circ = QuantumCircuit(3) # H gate on qubit 0, putting this qubit in a superposition of |0> + |1>. @@ -54,81 +80,850 @@ circ.draw('mpl') -Supplementary Information -========================= +.. _circuit-definitions: + +Circuit concepts and definitions +================================ + +There is a lot of specialized terminology around quantum circuits. Much of this is common in +quantum-computing literature, while some is more specific to quantum software packages, and a small +amount specific to Qiskit. This is an alphabetical listing of some of the important concepts as a +quick reference, but does not go into detail of the foundational concepts. Consider using the `IBM +Quantum Learning platform `_ if you want to start from the +beginning. + +abstract circuit + A *circuit* defined in terms of abstract mathematical operations and *virtual qubits*. This is + typically how you think about quantum algorithms; an abstract circuit can be made up of + completely arbitrary unitary operations, measurements, and potentially *real-time classical + computation*, with no restrictions about which qubits can interact with each other. + + You turn an abstract circuit into a *physical circuit* by using :ref:`Qiskit's transpilation + package `. + +ancilla qubit + An extra qubit that is used to help implement operations on other qubits, but whose final state + is not important for the program. + +circuit + A computational routine the defines a single execution to be taken on a QPU. This can either be + an *abstract circuit* or a *physical circuit*. + +clbit + A Qiskit-specific abbreviation meaning a single classical bit of data. + +gate + A *unitary operation* on one or more qubits. + +hardware qubit + The representation of a single qubit on a particular *QPU*. A hardware qubit has some physical + quantum-mechanical system backing it, such as superconducting circuits; unlike a *virtual + qubit*, it has particular coupling constraints and only certain gates can be applied to certain + groups of hardware qubits. + + Qiskit does not distinguish *logical qubits* from any individual *physical qubits* when talking + about hardware qubits. A QPU may implement its hardware qubits as logical qubits, where each + hardware qubit comprises many physical qubits that are controlled and error-corrected opaquely + to Qiskit by the control electronics. More likely, for near-term applications, a QPU will be + directly exposing its physical qubits as the hardware qubits for Qiskit to reason about. + + Both physical and logical qubits will have coupling constraints between them, only permit + certain quantum operations on them, and have scheduling concerns between them. Qiskit abstracts + these concerns together in the concept of hardware qubits. In the early days of quantum error + correction, particular backends may let you access their qubit resources either as high-level + logical qubits or as low-level physical qubits through Qiskit. + +instruction set architecture (ISA) + The abstract model of which operations are available on which sets of *hardware qubits* on one + particular *QPU*. For example, one QPU may allow :math:`\sqrt X` and :math:`R_Z` operations on + all single hardware qubits, and :math:`CX` operations on certain pairs of hardware qubits. + +logical qubit + A collection of several *physical qubits* that are controlled together by a QPU (from the user's + perspective) to apply real-time quantum error correction. A logical qubit is a type of + *hardware qubit* for Qiskit. + +measurement + The act of extracting one classical bit of a data from a single qubit state. This is an + irreversible operation, and usually destroys entanglement and phase coherence between the target + qubit and the rest of the system. + +physical circuit + A *circuit* defined in terms of *hardware qubits* and only the quantum operations available in a + particular *QPU's* *ISA*. Physical circuits are tied to one particular QPU architecture, and + will not run on other incompatible architectures. You may also hear this referred to as an *ISA + circuit*. + + You typically get a physical circuit by using :ref:`Qiskit's transpilation routines + ` on an *abstract circuit* that you constructed. + +physical qubit + A controllable two-level quantum system. This is literally one "physics" qubit, such as a + transmon or the electronic state of a trapped ion. A QPU may expose this directly as its + *hardware qubit*, or combine several physical qubits into a *logical qubit*. + +quantum processing unit (QPU) + Analogous to a CPU in classical computing or a GPU in graphics processing, a QPU is the hardware + that runs quantum operations on quantum data. You can always expect a QPU that uses the + *circuit* model of computation to be able to perform some set of *gates*, and *measurement* + operations. Depending on the particular technology, they also may be able to run some real-time + classical computations as well, such as classical control flow and bitwise calculations on + classical data. + +qubit + The basic unit of quantum information. + +real-time classical computation + Any classical computation that can happen within the execution of a single shot of a *circuit*, + where the results of the classical computation can affect later execution of the circuit. The + amount of real-time classical computation available with particular *QPU*\ s will vary + significantly dependent on many factors, such as the controlling electronics and the qubit + technology in use. You should consult your hardware vendor's documentation for more information + on this. + +unitary operation + A reversible operation on a quantum state. All quantum *gates* are unitary operations (by + definition). + +virtual qubit + An abstract, mathematical *qubit* used to build an *abstract circuit*. Virtual qubits are how + one typically thinks about quantum algorithms at a high level; we assume that all quantum gates + are valid on all virtual qubits, and all virtual qubits are always connected to every other + virtual qubit. + + When mapping to hardware, virtual qubits must be assigned to *hardware qubits*. This mapping + need not be one-to-one. Typically, one virtual qubit will need to be swapped from one hardware + qubit to another over the course of a circuit execution in order to satisfy coupling constraints + of the underlying QPU. It is not strictly necessary for all virtual qubits used in a circuit to + be mapped to a physical qubit at any given point in a *physical circuit*; it could be that a + virtual qubit is measured (collapsing its state) and then never used again, so a new virtual + qubit could take its place. Evaluating these conditions to map a virtual circuit to a physical + circuit is the job of :ref:`Qiskit's transpilation package `. + + +.. _circuit-module-api: + +API overview of :mod:`qiskit.circuit` +===================================== + +All objects here are described in more detail, and in their greater context in the following +sections. This section provides an overview of the API elements documented here. + +.. + TODO: actually write the "in-depth section on building circuits and cross-ref to it. + +The principal class is :class:`.QuantumCircuit`, which has its own documentation page, including +an in-depth section on building circuits. Quantum data and the simplest classical data are +represented by "bits" and "registers": + +* :class:`Bit`, an atom of data + * :class:`Qubit` + * :class:`Clbit` + * :class:`AncillaQubit` + +* :class:`Register`, a collection of bits + * :class:`QuantumRegister` + * :class:`ClassicalRegister` + * :class:`AncillaRegister` + +Within a circuit, each complete :class:`CircuitInstruction` is made up of an :class:`Operation` +(which might be an :class:`Instruction`, a :class:`Gate`, or some other subclass) and the qubit +and clbit operands. The core base classes here are: + +* :class:`CircuitInstruction`, an operation and its operands +* :class:`InstructionSet`, a temporary handle to a slice of circuit data +* :class:`Operation`, any abstract mathematical object or hardware instruction + * :class:`AnnotatedOperation`, a subclass with applied abstract modifiers + * :class:`InverseModifier` + * :class:`ControlModifier` + * :class:`PowerModifier` + +The most common concrete subclass of the minimal, abstract :class:`Operation` interface is the +:class:`Instruction`. While :class:`Operation` can include abstract mathematical objects, +an :class:`Instruction` is something that could conceivably run directly on hardware. This is in +turn subclassed by :class:`Gate` and :class:`ControlledGate` that further add unitarity and +controlled semantics on top: + +* :class:`Instruction`, representing a hardware-based instruction +* :class:`Gate`, representing a hardware instruction that is unitary +* :class:`ControlledGate`, representing a gate with control structure. + +Qiskit includes a large library of standard gates and circuits, which is documented in +:mod:`qiskit.circuit.library`. Many of these are declared as Python-object singletons. The +machinery for this is described in detail in :mod:`qiskit.circuit.singleton`, of which the main +classes are each a singleton form of the standard instruction–gate hierarchy: + +* :class:`~singleton.SingletonInstruction` +* :class:`~singleton.SingletonGate` +* :class:`~singleton.SingletonControlledGate` + +Some instructions are particularly special in that they affect the control flow or data flow of the +circuit. The top-level ones are: + +* :class:`Barrier`, to mark parts of the circuit that should be optimized independently +* :class:`Delay`, to insert a real-time wait period +* :class:`Measure`, to measure a :class:`Qubit` into a :class:`Clbit` +* :class:`Reset`, to irreversibly reset a qubit to the :math:`\lvert0\rangle` state +* :class:`Store`, to write a real-time classical expression to a storage location +* :class:`ControlFlowOp`, which has specific subclasses: + * :class:`BreakLoopOp`, to break out of the nearest containing loop + * :class:`ContinueLoopOp`, to move immediately to the next iteration of the containing loop + * :class:`ForLoopOp`, to loop over a fixed range of values + * :class:`IfElseOp`, to conditionally enter one of two subcircuits + * :class:`SwitchCaseOp`, to conditionally enter one of many subcicuits + * :class:`WhileLoopOp`, to repeat a subcircuit until a condition is falsified. + +:ref:`Circuits can include classical expressions that are evaluated in real time +`, while the QPU is executing a single shot of the circuit. These +are primarily documented in the module documentation of :mod:`qiskit.circuit.classical`. You might +be particularly interested in the base classes (which are not exposed from the :mod:`qiskit.circuit` +root): + +* :class:`~classical.expr.Var`, a typed classical storage location in a circuit +* :class:`~classical.expr.Expr`, a real-time-evaluated expression +* :class:`~classical.types.Type`, the classical type of an expression. + +In addition to this real-time expression evaluation, which is limited by classical hardware +representations of data, Qiskit has the concept of "compile-time" parametrization, which is done in +abstract symbolic algebra. These are typically used to represent gate angles in high-level +algorithms that might want to perform numerical derivatives, but they are an older part of Qiskit +than the real-time evaluation, so are still used in some places to do general parametrization. The +main related classes are: + +* :class:`Parameter`, the atom of compile-time expressions +* :class:`ParameterExpression`, a symbolic calculation on parameters +* :class:`ParameterVector`, a convenience collection of many :class:`Parameter`\ s + +The :mod:`qiskit.circuit` module also exposes some calculation classes that work with circuits to +assist compilation workflows. These include: -Quantum Circuit with conditionals ---------------------------------- +* :class:`EquivalenceLibrary`, a database of decomposition relations between gates and circuits +* :data:`SessionEquivalenceLibrary`, a mutable instance of :class:`EquivalenceLibrary` which is used + by default by the compiler's :class:`.BasisTranslator`. -When building a quantum circuit, there can be interest in applying a certain gate only -if a classical register has a specific value. This can be done with the -:meth:`InstructionSet.c_if` method. +There is also a utility for generating random circuits: + +* :func:`random.random_circuit` + +Finally, the circuit module has its own exception class, to indicate when things went wrong in +circuit-specific manners: + +* :exc:`CircuitError` + + +.. _circuit-repr: + +Representation of circuits in Qiskit +==================================== + +The main user-facing class for representing circuits is :class:`QuantumCircuit`. This can be either +an abstract circuit or a physical circuit. There is much more information about the +: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. -In the following example, we start with a single-qubit circuit formed by only a Hadamard gate -(:class:`~.HGate`), in which we expect to get :math:`|0\\rangle` and :math:`|1\\rangle` -with equal probability. +.. 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 +the particular operation (gate, measurement, etc) and its operands (the qubits and classical bits). + + +Bits and registers +------------------ + +Qubits and classical bits are represented by a shared base :class:`Bit` type, which is just intended +to be a "type tag"; the classes have no behavior other than being immutable objects: + +.. autoclass:: Bit +.. autoclass:: Qubit + :show-inheritance: + :class-doc-from: class +.. autoclass:: Clbit + :show-inheritance: + :class-doc-from: class + +Qubits and clbits are instantiated by users with no arguments, such as by ``Qubit()``. Bits compare +equal if they are the same Python object, or if they were both created by a register of the same +name and size, and they refer to the same index within that register. There is also a special type +tag for "ancilla" qubits, but this is little used in the current state +of Qiskit: + +.. autoclass:: AncillaQubit + :show-inheritance: + :class-doc-from: class + +A collection bits of the same type can be encapsulated in a register of the matching type. The base +functionality is in a base class that is not directly instantiated: + +.. autoclass:: Register + :members: + +Each of the defined bit subtypes has an associated register, which have the same constructor +signatures, methods and properties as the base class: + +.. autoclass:: QuantumRegister + :show-inheritance: + :class-doc-from: class +.. autoclass:: ClassicalRegister + :show-inheritance: + :class-doc-from: class +.. autoclass:: AncillaRegister + :show-inheritance: + :class-doc-from: class + +A common way to instantiate several bits at once is to create a register, such as by +``QuantumRegister("my_qreg", 5)``. This has the advantage that you can give that collection of bits +a name, which will appear during circuit visualizations (:meth:`QuantumCircuit.draw`) and exports to +interchange languages (see :mod:`.qasm2` and :mod:`.qasm3`). You can also pass a name and a list of +pre-constructed bits, but this creates an "aliasing register", which are very poorly supported on +hardware. + +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 +and strongly typed representation of a range of classical data (see +:ref:`circuit-repr-real-time-classical`), but you will still very commonly use classical bits in +current Qiskit. + + +Instruction contexts +-------------------- + +The scalar type of the :attr:`QuantumCircuit.data` sequence is the "instruction context" object, +:class:`CircuitInstruction`. This is essentially just a data class that contains a representation +of what is to be done (its :attr:`~CircuitInstruction.operation`), and the data it acts on (the +:attr:`~CircuitInstruction.qubits` and :attr:`~CircuitInstruction.clbits`). -.. plot:: - :include-source: +.. autosummary:: + :toctree: ../stubs/ - from qiskit import transpile, QuantumRegister, ClassicalRegister, QuantumCircuit - qr = QuantumRegister(1) - cr = ClassicalRegister(1) - qc = QuantumCircuit(qr, cr) - qc.h(0) - qc.measure(0, 0) - qc.draw('mpl') + CircuitInstruction -.. code-block:: +Programmatically, this class is actually implemented in Rust and is a constructed handle to internal +data within Rust space. Mutations to instances of this class will not be reflected in the circuit. +In general, you cannot mutate instruction contexts that are already in the circuit directly; the +:class:`QuantumCircuit` interface is designed for storing and building circuits, while the +:ref:`transpiler and its passes `, and its intermediate :class:`.DAGCircuit` +representation, are where you should look for an interface to mutate circuits. - from qiskit.providers.basic_provider import BasicSimulator - backend = BasicSimulator() - tqc = transpile(qc, backend) - counts = backend.run(tqc).result().get_counts() +The :class:`QuantumCircuit` methods that add instructions to circuits (such as +:meth:`~QuantumCircuit.append`, and all the helper standard-gate methods) return an +:class:`InstructionSet`, which is a handle to several :class:`CircuitInstruction`\ s simultaneously. - print(counts) +.. autosummary:: + :toctree: ../stubs/ -.. parsed-literal:: + InstructionSet - {'0': 524, '1': 500} +This :class:`InstructionSet` is now little used in Qiskit. It provides a very minimal set of +methods to perform post-append mutations on instructions (which *will* be propagated to the +circuit), but these are now discouraged and you should use the alternatives noted in those methods. -Now, we add an :class:`~.XGate` only if the value of the :class:`~.ClassicalRegister` is 0. -That way, if the state is :math:`|0\\rangle`, it will be changed to :math:`|1\\rangle` and -if the state is :math:`|1\\rangle`, it will not be changed at all, so the final state will -always be :math:`|1\\rangle`. -.. plot:: - :include-source: +Operations, instructions and gates +---------------------------------- - from qiskit import transpile, QuantumRegister, ClassicalRegister, QuantumCircuit +Within a :class:`CircuitInstruction`, the minimal interface that any operation must fulfill is +:class:`Operation`. This is a *very* high level view, and only usable for abstract circuits. The +main purpose of treating operations as :class:`Operation` is to allow arbitrary mathematical +objects (such as :class:`.quantum_info.Operator`) to be added to abstract circuits directly. - qr = QuantumRegister(1) - cr = ClassicalRegister(1) - qc = QuantumCircuit(qr, cr) - qc.h(0) - qc.measure(0, 0) +.. autosummary:: + :toctree: ../stubs/ - qc.x(0).c_if(cr, 0) - qc.measure(0, 0) + Operation - qc.draw('mpl') +Most operations, including all operations on physical circuits, are instances of the more concretely +defined :class:`Instruction`. This represents any instruction that some QPU might be able to carry +out natively, such as :class:`Measure`. :class:`Instruction` need not be unitary (much as +:class:`Measure` isn't); an instruction is specifically unitary if it is a :class:`Gate`. -.. code-block:: +.. autosummary:: + :toctree: ../stubs/ + + Instruction + +:class:`Instruction`\ s can be near arbitrary, provided they only act on :class:`Qubit`\ s and +:class:`Clbit`\ s, and are parametrized by their :attr:`~Instruction.params`; they should not +attempt to "close over" outer circuit registers, or use hidden parameters inside themselves. +:class:`Instruction`\ s can be related to other circuits to provide a decompositions by using +their :attr:`Instruction.definition` attribute, which provides a local, one-off decomposition. This +can be in whatever basis set of operations is most convenient to you, as long as the definitions of +all contained gates have some topological order; that is, you cannot use a gate in a definition it +its own definition depends on the parent. If the :class:`Instruction` should be considered entirely +opaque to optimizers, its :attr:`~Instruction.definition` can be ``None``. See +:ref:`circuit-custom-gates` for more detail. + +The :attr:`~Instruction.params` of an instruction can technically be arbitrary, but in general you +should attempt to stick to parametrizations in terms of real numbers, wherever possible. Qiskit +itself breaks this rule in many places, and you will find all sorts of unusual types in +:attr:`Instruction.params` fields, but these are an annoying source of bugs because they often imply +the need for type-aware special casing. If your instruction is parametrized in terms of angles, you +will be able to reliably use :ref:`compile-time parametrization in it +`, and it will integrate well with +:meth:`QuantumCircuit.assign_parameters`. + +While :class:`Instruction` is not necessarily unitary, its subclass :class:`Gate` implies unitarity, +and adds :meth:`~Gate.to_matrix` and :meth:`~Gate.control` methods to all the methods inherited from +:class:`Instruction`. - from qiskit.providers.basic_provider import BasicSimulator - backend = BasicSimulator() - tqc = transpile(qc, backend) - counts = backend.run(tqc).result().get_counts() +.. autosummary:: + :toctree: ../stubs/ - print(counts) + Gate -.. parsed-literal:: +:class:`Gate` inherits all the methods for :class:`Instruction` and all the same considerations +about its :attr:`~Instruction.params` and :attr:`~Instruction.definition` field, except of course +that :class:`Gate`\ s cannot act on any classical resources. - {'1': 1024} +:class:`Gate` instances can (and should) have a base :attr:`~Instruction.definition`, but you can +also specify several different decompositions in different bases by using an +:class:`EquivalenceLibrary`. +Subclassing :class:`Gate`, Qiskit has a special :class:`ControlledGate` class as well. This class +is the base of many standard-library gates that are controlled (such as :class:`CXGate`), which is +where you are most likely to encounter it: -Quantum Circuit Properties --------------------------- +.. autosummary:: + :toctree: ../stubs/ + + ControlledGate + +Each of :class:`Instruction`, :class:`Gate` and :class:`ControlledGate` has a corresponding +singleton type, built using the machinery described in :mod:`qiskit.circuit.singleton`. The +module-level documentation contains full details, along with descriptions of +:class:`.SingletonInstruction`, :class:`.SingletonGate` and :class:`.SingletonControlledGate`. From +a user's perspective, little changes based on whether the base class is a singleton or not; the +intention always remains that you should call :meth:`~Instruction.to_mutable` first if you need to +get a safe-to-mutate owned copy of an instruction (you cannot assume that an arbitrary instruction +is mutable), and while direct :class:`type` inspection is discouraged, if you do need it, the +reliable way to find the "base" type of a potentially singleton instruction is to use +:attr:`~Instruction.base_class`. + +:class:`ControlledGate` uses the same mechanisms as :ref:`subclassing gates ` +to define a fixed, lazy synthesis for itself. This is naturally not hardware-aware, and harder to +hook into the synthesis routines of the compiler, but works better as a concrete +:class:`Instruction` that could potentially be run natively on hardware. For cases where synthesis +and abstract optimization is more important, Qiskit offers a composable class called +:class:`AnnotatedOperation`, which tracks "gate modifiers" (of which :class:`ControlModifier` is +one) to apply to the inner :attr:`~AnnotatedOperation.base_op`. + +.. autosummary:: + :toctree: ../stubs/ + + AnnotatedOperation + +The available modifiers for :class:`AnnotatedOperation` are: + +.. autoclass:: InverseModifier +.. autoclass:: ControlModifier +.. autoclass:: PowerModifier + +For information on how to create custom gates and instructions, including how to build one-off +objects, and re-usable parametric gates via subclassing, see :ref:`circuit-custom-gates` below. +The Qiskit circuit library in :mod:`qiskit.circuit.library` contains many predefined gates and +circuits for you to use. + + +Built-in special instructions +----------------------------- + +Qiskit contains a few :class:`Instruction` classes that are in some ways "special". These typically +have special handling in circuit code, in the transpiler, or the models of hardware. These are all +generally instructions you might already be familiar with. + +Measurements in Qiskit are of a single :class:`Qubit` into a single :class:`Clbit`. These are the +two that the instruction is applied to. Measurements are in the computational basis. + +.. autoclass:: Measure(label=None) + :show-inheritance: + +Related to measurements, there is a :class:`Reset` operation, which produces no classical data but +instructs hardware to return the qubit to the :math:`\lvert0\rangle` state. This is assumed to +happen incoherently and to collapse any entanglement. + +.. autoclass:: Reset(label=None) + :show-inheritance: + +Hardware can be instructed to apply a real-time idle period on a given qubit. A scheduled circuit +(see :mod:`qiskit.transpiler`) will include all the idle times on qubits explicitly in terms of this +:class:`Delay`. + +.. autoclass:: Delay + :show-inheritance: + +The :class:`Barrier` instruction can span an arbitrary number of qubits and clbits, and is a no-op +in hardware. During transpilation and optimization, however, it blocks any optimizations from +"crossing" the barrier; that is, in:: + + from qiskit.circuit import QuantumCircuit + + qc = QuantumCircuit(1) + qc.x(0) + qc.barrier() + qc.x(0) + +it is forbidden for the optimizer to cancel out the two :math:`X` instructions. + +.. autoclass:: Barrier + :show-inheritance: + +The :class:`Store` instruction is particularly special, in that it allows writing the result of a +:ref:`real-time classical computation expression ` (an +:class:`.expr.Expr`) in a local classical variable (a :class:`.expr.Var`). It takes *neither* +:class:`Qubit` nor :class:`Clbit` operands, but has an explicit :attr:`~Store.lvalue` and +:attr:`~Store.rvalue`. + +.. autoclass:: Store + :show-inheritance: + :members: + :no-inherited-members: + + +.. _circuit-repr-real-time-classical: + +Real-time classical computation +------------------------------- + +.. note:: + + 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. + +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 +support for these concepts as well, so beware that you will need to work closely with your hardware +provider's documentation to get the best use out of any real-time classical computation. + +These real-time calculations are represented by the expression and type system in +:mod:`qiskit.circuit.classical`. At a high level, all real-time expressions are represented by an +:class:`.Expr` node, which is part of an expression "tree" representation, which has a well-defined +:class:`~.classical.Type` associated with it at every level. See the module-level documentation for +much more detail on the internal representations of these classes. + +The result of a real-time :class:`.Expr` can be used directly in certain places. Currently this is +limited to conditions of :class:`.IfElseOp` and :class:`.WhileLoopOp`, and the target of +:class:`.SwitchCaseOp`. The result can also be stored in a typed classical storage location, using +the :class:`.Store` instruction (or its :meth:`QuantumCircuit.store` constructor), backed by a +:class:`.expr.Var` node. + +A circuit can contain manual classical storage locations, represented internally by the +:class:`~.expr.Var` node of the :class:`.Expr` tree. These have an attached classical type (like +any other expression). These can either be declared and initialized within each execution of the +circuit (:meth:`~QuantumCircuit.add_var`), or be inputs to the circuit +(:meth:`~QuantumCircuit.add_input`). + +.. _circuit-compile-time-parameters: + +Compile-time parametrization +---------------------------- + +Various parametric :class:`Instruction` instances in Qiskit can be parametrized in ways that are +designed to be resolved at compile time. These are characterized by the use of the +:class:`Parameter` and :class:`ParameterExpression` classes. + +.. autosummary:: + :toctree: ../stubs/ + + Parameter + ParameterExpression + +The main way that this differs from the :class:`expr.Var` variables used in real-time classical +computation is that :class:`ParameterExpression` is a symbolic representation of a mathematical +expression. The semantics of the expression are those of regular mathematics over the continuous +real numbers (and, in limited cases, over the complex numbers). In contrast, :class:`.Var` is a +handle to a variable stored on a classical computer, such as a floating-point value or an +fixed-width integer, which are always discrete. + +In other words, you can expect :class:`ParameterExpression` to do symbolic simplifications that are +valid in mathematics, such as simplifying :math:`(x + y - x) / y \to 1`. Such a simplification is +not valid in floating-point arithmetic, and :class:`.expr.Expr` will not do this. + +The "compile-time" part of these parameters means that you typically will want to "assign" values to +the parameters before sending the circuit for execution. These parameters can typically be used +anywhere that expects a mathematical angle (like a rotation gate's parameters), with the caveat that +hardware will usually require them to be assigned to a proper classically typed value before +execution. You can do this assignment using :meth:`QuantumCircuit.assign_parameters`. + +You may want to use many parameters that are related to each other. To make this easier (and to +avoid you needing to come up with many names), you can use the convenience constructor +:class:`ParameterVector`. The elements of the vector are all valid :class:`Parameter` instances. + +.. autosummary:: + :toctree: ../stubs/ + + ParameterVector + +.. _circuit-control-flow: + +Control flow in circuits +------------------------ + +Within :class:`QuantumCircuit`, classical control flow is represented by specific +:class:`Instruction`\ s, which are subclasses of :class:`ControlFlowOp`. + +.. autosummary:: + :toctree: ../stubs/ + + ControlFlowOp + +These control-flow operations (:class:`IfElseOp`, :class:`WhileLoopOp`, +:class:`SwitchCaseOp` and :class:`ForLoopOp`) all have specific state that defines the branching +conditions and strategies, but contain all the different subcircuit blocks that might be entered in +their :attr:`~ControlFlowOp.blocks` property. + +.. autosummary:: + :toctree: ../stubs/ + + IfElseOp + WhileLoopOp + SwitchCaseOp + ForLoopOp + +The :class:`.SwitchCaseOp` also understands a special value: + +.. autodata:: CASE_DEFAULT + +In addition to the block-structure control-flow operations, there are also two special instructions +that affect the flow of control when within loops. These correspond to typical uses of the +``break`` and ``continue`` statements in classical programming languages. + +.. autosummary:: + :toctree: ../stubs/ + + BreakLoopOp + ContinueLoopOp + +.. note:: + 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. + +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 +qubits and clbits in its :attr:`~CircuitInstruction.qubits` and :attr:`~CircuitInstruction.clbits` +attributes. This can lead to subtle data-coherence problems: the :class:`Qubit` and :class:`Clbit` +objects used inside the subcircuit blocks of the control-flow ops will not necessarily be identical +to the corresponding objects in the :class:`CircuitInstruction`. Any code that consumes +control-flow operations in Qiskit needs to be aware of this; within a subcircuit, you should treat +``subcircuit.qubits[i]`` as if it were really ``outer_instruction.qubits[i]``, and so on. You can +generate an easy lookup table for this by doing:: + + cf_instruction: CircuitInstruction = ... + cf_operation: ControlFlowOp = cf_instruction.operation + for block in blocks: + # Mappings of "inner" qubits/clbits to the outer ones. + qubit_map = dict(zip(block.qubits, cf_instruction.qubits)) + clbit_map = dict(zip(block.clbits, cf_instruction.clbits)) + + # ... do something with `block` ... + +Remember that you will need to propagate this information if you recurse into subblocks of +control-flow operations. + +.. + TODO: insert cross-ref to control-flow builder guide into below paragraph once written. + +All the subcircuit blocks in a :class:`ControlFlowOp` are required to contain the same numbers of +:class:`Qubit`\ s and :class:`Clbit`\ s, referring to the same outer bits in the same order, such +that the :class:`zip` loop given in the code block above works. The inner-circuit :class:`Bit` +objects do not need to be literally the same objects. When using the control-flow builder interface +(which, it cannot be stressed enough, is *highly* recommended for users), the builders will arrange +that the inner bit objects *are* identical to the outer bit objects; the ``qubit_map`` in the code +block above will always be a mapping ``{x: x}``, but if you are consuming the blocks, you should be +prepared for the case that the mapping is required. + +Any :class:`ClassicalRegister`\ s used in a control-flow subcircuit must also be present in all +containing blocks (*i.e.* any containing control-flow operations, and the outermost circuit), and +all blocks in the same :class:`ControlFlowOp` need to contain the same registers. Again, the +builder interface will arrange for this to be the case (or produce an eager error if they cannot). + +When the low-level construction is being used the inner :class:`QuantumCircuit` blocks must +manually close over any outer-scope :ref:`real-time classical computation variables +` that they use. This is marked by these being in the +:meth:`~QuantumCircuit.iter_captured_vars` iterator for that block. Libraries constructing these +blocks manually will need to track these captures when building control-flow circuit blocks and add +them to the block using :meth:`~QuantumCircuit.add_capture` (or the ``captures`` constructor +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. + +.. _circuit-custom-gates: + +Creating custom instructions +============================ + +If you wish to create simple one-off instructions or gates that will be added to a circuit, and the +blocks are just being used for visualization or grouping purposes, the easiest way to create a +custom instruction or gate is simply to build its definition as a :class:`QuantumCircuit`, and then +use its :meth:`~QuantumCircuit.to_instruction` or :meth:`~QuantumCircuit.to_gate` method as +appropriate. The results can be given directly to :meth:`QuantumCircuit.append` on the larger +circuit. These methods will create base :class:`Instruction` or :class:`Gate` instances whose +:attr:`~Instruction.definition` attribute is the circuit as supplied, meaning it will automatically +be accessible to the transpiler, and to other Qiskit functions that attempt to decompose circuits. + +Note that standalone instructions and gates should act only on qubits and clbits; instructions that +need to use complex control-flow will need to be inlined onto the :class:`QuantumCircuit` using +:meth:`~QuantumCircuit.compose`. + + +Creating instruction subclasses +------------------------------- + +The base classes :class:`Instruction`, :class:`Gate` and :class:`ControlledGate` are all designed to +be safe to subclass, and have hook points for subclasses to implement. If your custom gate is +parameterless and stateless, you may also want to derive from the corresponding singleton class in +:mod:`qiskit.circuit.singleton`, such as :class:`SingletonGate`. You should consult the +documentation in :mod:`qiskit.circuit.singleton` for additional methods and hook points for the +singleton machinery. + +Subclasses should typically define a default constructor that calls the :class`super` constructor +with the correct arguments for your instruction. It is permissible to have extra state in the +class, but your subclasses will most reliably integrate with the rest of the Qiskit machinery if you +depend only on your :attr:`Instruction.params`, and these parameters are purely gate angles. + +Subclasses of :class:`Instruction` (or one of its subclasses) should implement the private +:meth:`Instruction._define` method, which lazily populates the hidden ``_definition`` cache that +backs the public :attr:`~Instruction.definition` method. + +.. automethod:: Instruction._define + +In subclasses of :class:`ControlledGate`, the :meth:`~Instruction._define` method should implement +the decomposition only for the all-ones control state. The :attr:`ControlledGate.definition +` machinery will modify this to handle the actual control state. + +If the subclass is using the singleton machinery, beware that :meth:`~Instruction._define` will be +called eagerly immediately after the class-body statement has been executed, in order to produce the +definition object for the canonical singleton object. This means that your definition must only use +gates that are already defined; if you are writing a library with many singleton gates, you will +have to order your files and imports to ensure that this is possible. + +Subclasses of :class:`Gate` will also likely wish to override `the Numpy array-protocol instance +method `__, +``__array__``. This is used by :meth:`Gate.to_matrix`, and has the signature: + +.. currentmodule:: None +.. py:method:: __array__(dtype=None) + + Return a Numpy array representing the gate. This can use the gate's :attr:`~Instruction.params` + field, and may assume that these are numeric values (assuming the subclass expects that) and not + :ref:`compile-time parameters `. + + For greatest efficiency, the returned array should default to a dtype of :class:`complex`. +.. currentmodule:: qiskit.circuit + +If your custom subclass has natural representations of its controlled or inverse forms, you may also +wish to override the :meth:`~Instruction.inverse` and :meth:`~Gate.control` methods. + + +As an example of defining a custom :math:`R_{xz}` gate; that is, a single-angle rotation about the +:math:`XZ` axis. This is essentially :class:`RZXGate`, if the qubits were the other way around, so +we will write our definition in terms of that. We are parametric, so cannot be a singleton, but we +are unitary, so should be a :class:`Gate`:: + + import math + import numpy as np + from qiskit.circuit import Gate, QuantumCircuit + + class RXZGate(Gate): + def __init__(self, theta): + # Initialize with our name, number of qubits and parameters. + super().__init__("rxz", 2, [theta]) + + def _define(self): + # Our base definition is an RZXGate, applied "backwards". + defn = QuantumCircuit(2) + defn.rzx(1, 0) + self._definition = defn + + def inverse(self, annotated = False): + # We have an efficient representation of our inverse, + # so we'll override this method. + return RXZGate(-self.params[0]) + + def power(self, exponent: float): + # Also we have an efficient representation of power. + return RXZGate(exponent * self.params[0]) + + def __array__(self, dtype=None): + cos = math.cos(0.5 * self.params[0]) + isin = 1j * math.sin(0.5 * self.params[0]) + return np.array([ + [cos, -isin, 0, 0], + [-isin, cos, 0, 0], + [0, 0, cos, isin], + [0, 0, isin, cos], + ], dtype=dtype) + + +In this example, we defined a base definition in terms of :class:`RZXGate`, but to enable faster +decompositions to a range of bases, we might want to add some more equivalences to +:data:`SessionEquivalenceLibrary`. Note that the :class:`.BasisTranslator` translation search will +search through all possible equivalences at all possible depths, so providing an equivalence in +terms of (say) :class:`.XGate` will automatically make decompositions in terms of :class:`.RXGate` +available as well. + +Let us add an equivalence in terms of :math:`H`, :math:`CX` and :math:`R_z` for an arbitrary symbolic +parameter:: + + from qiskit.circuit import SessionEquivalenceLibrary, Parameter + + theta = Parameter("theta") + + equiv = QuantumCircuit(2) + equiv.h(0) + equiv.cx(1, 0) + equiv.rz(theta, 0) + equiv.cx(1, 0) + equiv.h(0) + + SessionEquivalenceLibrary.add_equivalence(RZXGate(theta), equiv) + +After this, for the duration of the Python interpreter session, translators like +:class:`.BasisTranslator` will find our new definition in their search. + + +.. _circuit-working-with: + +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. @@ -229,11 +1024,6 @@ .. image:: /source_images/depth.gif -.. raw:: html - -

- - We can verify our graphical result using :meth:`QuantumCircuit.depth`: .. code-block:: @@ -244,122 +1034,309 @@ 9 -.. raw:: html +.. _circuit-abstract-to-physical: -
+Converting abstract circuits to physical circuits +------------------------------------------------- -Quantum Circuit API -=================== +.. + Note that this is just a "jumping-off" section - this should just provide an overview of links + to where the real information is. -Quantum Circuit Construction ----------------------------- +An abstract :class:`QuantumCircuit` cannot reliably be run on hardware. You might be able to use +some of the high-level simulators linked to in :ref:`circuit-simulation` to produce quick results +for small scale circuits, but to run utility-scale circuits, you will need to use real hardware, +which involves compiling to a physical circuit. -.. autosummary:: - :toctree: ../stubs/ - - QuantumCircuit - QuantumRegister - Qubit - ClassicalRegister - Clbit - AncillaRegister - AncillaQubit - CircuitInstruction - Register - Bit - -Gates and Instructions ----------------------- +The high-level function to do this is :func:`.transpile`; it takes in an abstract circuit and a +hardware ``backend`` or ``target``, and returns a physical circuit. To get more access and control +over the stages of the passes that will be run, use :func:`.generate_preset_pass_manager` to build a +:class:`~.transpiler.StagedPassManager` first, which you can then modify. -.. autosummary:: - :toctree: ../stubs/ +The full transpilation and compilation machinery is described in detail in the +:mod:`qiskit.transpiler` module documentation, and detail on all the passes built into Qiskit is +available in :mod:`qiskit.transpiler.passes`. - Gate - ControlledGate - Delay - Instruction - InstructionSet - Operation - EquivalenceLibrary - Store -Control Flow Operations ------------------------ +.. _circuit-simulation: -.. autosummary:: - :toctree: ../stubs/ +Simulating circuits +------------------- - ControlFlowOp - IfElseOp - WhileLoopOp - ForLoopOp - SwitchCaseOp - BreakLoopOp - ContinueLoopOp +.. + Note that this is just a "jumping-off" section - this should just provide an overview of links + to where the real information is. -The :class:`.SwitchCaseOp` also understands a special value: -.. py:data:: CASE_DEFAULT +While not part of the :mod:`qiskit.circuit` interface, one of the most common needs is to get quick +simulation results for :class:`QuantumCircuit` objects. This section provides a quick jumping-off +point to other places in the documentation to find the relevant information. - A special object that represents the "default" case of a switch statement. If you use this as a - case target, it must be the last case, and will match anything that wasn't already matched. For - example:: +For unitary circuits, you can simulate the effects on the :math:`\lvert0\dotsm0\rangle` state by +passing the :class:`QuantumCircuit` directly to the :class:`~.quantum_info.Statevector` default +constructor. You can similar get a unitary matrix representing the circuit as an operator by +passing it to the :class:`~.quantum_info.Operator` default constructor. If you have a physical +circuit, you may want to instead pass it to :meth:`.Operator.from_circuit` method to apply +transformations from the :attr:`QuantumCircuit.layout` to map it back to the "abstract" qubit space. - from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister - from qiskit.circuit import SwitchCaseOp, CASE_DEFAULT +For a more backend-like simulation experience, there are simulator-backed implementations of all the +Qiskit hardware interfaces. In particular, you might be interested in: - body0 = QuantumCircuit(2, 2) - body0.x(0) - body1 = QuantumCircuit(2, 2) - body1.z(0) - body2 = QuantumCircuit(2, 2) - body2.cx(0, 1) +* :class:`.BasicProvider` and the raw backends it can return to you. +* :class:`.StatevectorSimulator` for a backend-like wrapper around :class:`.Statevector` +* The :mod:`qiskit_aer` for full, high-performance simulation capabilities. +* :class:`.StatevectorSampler` and :class:`.StatevectorEstimator` for simulator-backed reference + implementations of the :ref:`Qiskit Primitives `. - qr, cr = QuantumRegister(2), ClassicalRegister(2) - qc = QuantumCircuit(qr, cr) - qc.switch(cr, [(0, body0), (1, body1), (CASE_DEFAULT, body2)], qr, cr) - When using the builder interface of :meth:`.QuantumCircuit.switch`, this can also be accessed as - the ``DEFAULT`` attribute of the bound case-builder object, such as:: +Defining equivalence relationships +---------------------------------- - from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister +A common task in mapping abstract circuits to physical hardware and optimizing the result is to find +equivalence relations that map a gate to a different basis set. Qiskit stores this information in a +database class called :class:`EquivalenceLibrary`. - qr, cr = QuantumRegister(2), ClassicalRegister(2) - qc = QuantumCircuit(qr, cr) - with qc.switch(cr) as case: - with case(0): - qc.x(0) - with case(1): - qc.z(0) - with case(case.DEFAULT): - qc.cx(0, 1) +.. autosummary:: + :toctree: ../stubs/ + EquivalenceLibrary -Parametric Quantum Circuits ---------------------------- +Qiskit ships with a large set of predefined equivalence relationships for all of its standard gates. +This base library is called :data:`StandardEquivalenceLibrary`, and should be treated as immutable. -.. autosummary:: - :toctree: ../stubs/ +.. py:data:: StandardEquivalenceLibrary + + A :class:`EquivalenceLibrary` that stores of all Qiskit's built-in standard gate relationships. + You should not mutate this, but instead either create your own :class:`EquivalenceLibrary` using + this one as its ``base``, or modify the global-state :data:`SessionEquivalenceLibrary`. + +Qiskit also defines a shared global-state object, :data:`SessionEquivalenceLibrary`, which is the +default equivalences used by various places in Qiskit, most notably the :class:`.BasisTranslator` +transpiler pass. You should feel free to add your own equivalences to this using its +:meth:`~EquivalenceLibrary.add_equivalence` method, and they will be automatically picked up by +default instances of the :class:`.BasisTranslator`. + +.. py:data:: SessionEquivalenceLibrary - Parameter - ParameterVector - ParameterExpression + The default instance of :class:`EquivalenceLibrary`, which will be used by most Qiskit objects + if no library is manually specified. You can feel free to add equivalences to this using + :meth:`~EquivalenceLibrary.add_equivalence`. It inherits all the built-in rules of + :data:`StandardEquivalenceLibrary`. -Random Circuits ---------------- + + +Generating random circuits +-------------------------- + +.. + If we expand these capabilities in the future, it's probably best to move it to its own + module-level documentation page than to expand this "inline" module documentation. .. currentmodule:: qiskit.circuit.random .. autofunction:: random_circuit .. currentmodule:: qiskit.circuit + Exceptions ----------- +========== Almost all circuit functions and methods will raise a :exc:`CircuitError` when encountering an error that is particular to usage of Qiskit (as opposed to regular typing or indexing problems, which will typically raise the corresponding standard Python error). .. autoexception:: CircuitError + + +.. _circuit-conventions: + +Circuit conventions +=================== + +When constructing circuits out of abstract objects and more concrete matrices, there are several +possible conventions around bit-labelling, bit-ordering, and how the abstract tensor product is +realized in concrete matrix algebra. + +Qiskit's conventions are: + +* in bitstring representations, bits are labelled with the right-most bit in the string called + :math:`0` and the left-most bit in the string of :math:`n` bits called :math:`n - 1`. + +* when using integers as bit-specifier indices in circuit-construction functions, the integer is + treated as an index into :attr:`QuantumCircuit.qubits` (or :attr:`~QuantumCircuit.clbits`). + +* when drawing circuits, we put the lowest-index bits on top. + +* in statevector representations, we realize the abstract tensor product as the Kronecker product, + and order the arguments to this such that the amplitude of computational-basis state + :math:`\lvert x\rangle`, where :math:`x` is the bitstring interpreted as an integer, is at + location ``statevector[x]``. + +* when controlling a gate, the control qubit(s) is placed first in the argument list, *e.g.* in the + call ``qc.cx(0, 1)``, qubit 0 will be the control and qubit 1 will be the target. Similarly, in + the manual call ``qc.append(CXGate(), [0, 1])``, qubit 0 will be the control and qubit 1 will be + the target. + +Let us illustrate these conventions with some examples. + +Bit labelling +------------- + +Take the circuit: + +.. plot:: + :include-source: + :nofigs: + :context: + :show-source-link: False + + from qiskit import QuantumCircuit + + qc = QuantumCircuit(5, 5) + qc.x(0) + qc.x(1) + qc.x(4) + qc.measure(range(5), range(5)) + +This flips the states of qubits 0, 1 and 4 from :math:`\lvert0\rangle` to :math:`\lvert1\rangle`, +then measures all qubits :math:`n` into the corresponding clbit :math:`n` using the computational +(:math:`Z`) basis. If simulated noiselessly, the bitstring output from this circuit will be +:math:`10011` every time; qubits 0, 1, and 4 are flipped, and the "one" values in the bitstring are +in the zeroth, first and fourth digits *from the right*. + +In Qiskit, we would write the qubit state immediately before the measurement in ket-notation +shorthand as :math:`\lvert10011\rangle`. Note that the ket label matches the classical bitstring, +and has the numeric binary value of 19. + +If we draw this circuit, we will see that Qiskit places the zeroth qubit on the top of the circuit +drawing: + +.. plot:: + :include-source: + :context: + :show-source-link: False + + qc.draw("mpl") + + +Matrix representations +---------------------- + +Statevectors are defined in the convention that for a two-level system, the relationship between +abstract representation and matrix representation is such that + +.. math:: + + \alpha\lvert0\rangle + \beta\lvert1\rangle + \leftrightarrow \begin{pmatrix} \alpha \\ \beta \end{pmatrix} + +where :math:`\alpha` and :math:`\beta` are complex numbers. We store the statevector as a 1D Numpy +:class:`~numpy.ndarray` with data ``sv = [alpha, beta]``, *i.e.* ``sv[0] == alpha`` and ``sv[1] == +beta``; note that the indices into the statevector match the ket labels. + +We construct `the tensor product of two qubit states +`_ in matrix algebra using `the Kronecker product +`_, with qubit 0 on the right and qubit 1 on the +left, such that the :math:`Z` basis state :math:`\lvert x\rangle` (where :math:`x` is the integer +interpretation of the bitstring) has its non-zero term in the statevector ``sv`` at ``sv[x]``:: + + import numpy + from qiskit import QuantumCircuit + from qiskit.quantum_info import Statevector + + state_0 = [1, 0] # defined representation of |0> + state_1 = [0, 1] # defined representation of |1> + + # Circuit that creates basis state |10011>, where + # binary 10011 has the decimal value 19. + qc = QuantumCircuit(5) + qc.x(0) + qc.x(1) + qc.x(4) + qiskit_sv = Statevector(qc) + + # List index 'n' corresponds to qubit 'n'. + individual_states = [ + state_1, + state_1, + state_0, + state_0, + state_1, + ] + # Start from a scalar. + manual_sv = [1] + for qubit_state in individual_states: + # Each new qubit goes "on the left". + manual_sv = numpy.kron(qubit_state, manual_sv) + + # Now `qiskit_sv` and `manual_sv` are the same, and: + assert manual_sv[19] == 1 + assert qiskit_sv[19] == 1 + +This feeds through to the matrix representation of operators, and joins with the conventions on bit +orders for controlled operators. For example, the matrix form of :class:`.CXGate` is:: + + import numpy + from qiskit.circuit.library import CXGate + + numpy.array(CXGate()) + +.. math:: + + \operatorname{array}(CX) = + \begin{pmatrix} + 1 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 1 \\ + 0 & 0 & 1 & 0 \\ + 0 & 1 & 0 & 0 + \end{pmatrix} + +This might be different to other matrix representations you have seen for :math:`CX`, but recall +that the choice of matrix representation is conventional, and this form matches Qiskit's conventions +of *control qubits come first* and *the tensor product is represented such that there is a +correspondence between the index of the "one amplitude" and the bitstring value of a state*. + +In the case of multiple controls for a gate, such as for :class:`.CCXGate`, the ``ctrl_state`` +argument is interpreted as the bitstring value of the control qubits, using the same zero-based +labelling conventions. For example, given that the default ``ctrl_state`` is the all-ones +bitstring, we can see that the matrix form of :class:`.CCXGate` with ``ctrl_state = 1`` is the same +as if we took the all-ones control-state :class:`.CCXGate`, but flipped the value of the higher +indexed control qubit on entry and exist to the gate:: + + from qiskit import QuantumCircuit + from qiskit.quantum_info import Operator + + # Build the natural representation of `CCX` with the + # control qubits being `[0, 1]`, relative to the + # bitstring state "01", such that qubit 0 must be in |1> + # and qubit 1 must be in |0>. The target qubit is 2. + ccx_natural = QuantumCircuit(3) + ccx_natural.ccx(0, 1, 2, ctrl_state=1) + + # Build the same circuit in terms of the all-ones CCX. + # Note that we flip _qubit 1_, because that's the one + # that differs from the all-ones state. + ccx_relative = QuantumCircuit(3) + ccx_relative.x(1) + ccx_relative.ccx(0, 1, 2) + ccx_relative.x(1) + + assert Operator(ccx_relative) == Operator(ccx_natural) + +In both these cases, the matrix form of :class:`.CCXGate` in ``ctrl_state = 1`` is: + +.. math:: + + \operatorname{array}\bigl(CCX(\text{ctrl\_state}=1)\bigr) = + \begin{pmatrix} + 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 \\ + 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ + 0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\ + 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 \\ + 0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 + \end{pmatrix} """ from .exceptions import CircuitError @@ -387,6 +1364,7 @@ from .bit import Bit from .register import Register from . import library +from .equivalence_library import StandardEquivalenceLibrary, SessionEquivalenceLibrary from .commutation_checker import CommutationChecker from .controlflow import ( diff --git a/qiskit/circuit/_classical_resource_map.py b/qiskit/circuit/_classical_resource_map.py index 2de27ecd7612..cfbdd077bda4 100644 --- a/qiskit/circuit/_classical_resource_map.py +++ b/qiskit/circuit/_classical_resource_map.py @@ -110,8 +110,8 @@ def map_condition(self, condition, /, *, allow_reorder=False): return (mapped_theirs, mapped_value) def map_target(self, target, /): - """Map the runtime variables in a ``target`` of a :class:`.SwitchCaseOp` to the new circuit, - as defined in the ``circuit`` argument of the initialiser of this class.""" + """Map the real-time variables in a ``target`` of a :class:`.SwitchCaseOp` to the new + circuit, as defined in the ``circuit`` argument of the initialiser of this class.""" if isinstance(target, Clbit): return self.bit_map[target] if isinstance(target, ClassicalRegister): diff --git a/qiskit/circuit/_utils.py b/qiskit/circuit/_utils.py index de395965a854..cfde85bad8dd 100644 --- a/qiskit/circuit/_utils.py +++ b/qiskit/circuit/_utils.py @@ -13,6 +13,7 @@ This module contains utility functions for circuits. """ +import math import numpy from qiskit.exceptions import QiskitError from qiskit.circuit.exceptions import CircuitError @@ -56,7 +57,7 @@ def _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=None): Raises: QiskitError: unrecognized mode or invalid ctrl_state """ - num_target = int(numpy.log2(base_mat.shape[0])) + num_target = int(math.log2(base_mat.shape[0])) ctrl_dim = 2**num_ctrl_qubits ctrl_grnd = numpy.repeat([[1], [0]], [1, ctrl_dim - 1]) if ctrl_state is None: diff --git a/qiskit/circuit/annotated_operation.py b/qiskit/circuit/annotated_operation.py index 2e7e4405e6be..1481b24dc0e1 100644 --- a/qiskit/circuit/annotated_operation.py +++ b/qiskit/circuit/annotated_operation.py @@ -97,7 +97,10 @@ def __init__(self, base_op: Operation, modifiers: Union[Modifier, List[Modifier] inverted and then controlled by 2 qubits. """ self.base_op = base_op + """The base operation that the modifiers in this annotated operation applies to.""" self.modifiers = modifiers if isinstance(modifiers, List) else [modifiers] + """Ordered sequence of the modifiers to apply to :attr:`base_op`. The modifiers are applied + in order from lowest index to highest index.""" @property def name(self): diff --git a/qiskit/circuit/barrier.py b/qiskit/circuit/barrier.py index dd02095a91bc..c339066b4dce 100644 --- a/qiskit/circuit/barrier.py +++ b/qiskit/circuit/barrier.py @@ -16,29 +16,26 @@ with the :meth:`~qiskit.circuit.QuantumCircuit.barrier` method. """ +from __future__ import annotations + from qiskit.exceptions import QiskitError from .instruction import Instruction class Barrier(Instruction): - """Barrier instruction. + """A directive for circuit compilation to separate pieces of a circuit so that any optimizations + or re-writes are constrained to only act between barriers. - A barrier is a visual indicator of the grouping of a circuit section. - It also acts as a directive for circuit compilation to separate pieces - of a circuit so that any optimizations or re-writes are constrained - to only act between barriers.""" + This will also appear in visualizations as a visual marker. + """ _directive = True - def __init__(self, num_qubits, label=None): - """Create new barrier instruction. - + def __init__(self, num_qubits: int, label: str | None = None): + """ Args: - num_qubits (int): the number of qubits for the barrier type [Default: 0]. - label (str): the barrier label - - Raises: - TypeError: if barrier label is invalid. + num_qubits: the number of qubits for the barrier. + label: the optional label of this barrier. """ self._label = label super().__init__("barrier", num_qubits, 0, [], label=label) diff --git a/qiskit/circuit/bit.py b/qiskit/circuit/bit.py index fd5ce54cc464..bd278850f46e 100644 --- a/qiskit/circuit/bit.py +++ b/qiskit/circuit/bit.py @@ -24,7 +24,6 @@ class Bit: .. note:: This class should not be instantiated directly. This is just a superclass for :class:`~.Clbit` and :class:`~.circuit.Qubit`. - """ __slots__ = {"_register", "_index", "_hash", "_repr"} diff --git a/qiskit/circuit/classical/__init__.py b/qiskit/circuit/classical/__init__.py index 9bc4ac8b3f54..d259c9ba8c74 100644 --- a/qiskit/circuit/classical/__init__.py +++ b/qiskit/circuit/classical/__init__.py @@ -15,8 +15,8 @@ Classical expressions (:mod:`qiskit.circuit.classical`) ======================================================= -This module contains an exploratory representation of runtime operations on classical values during -circuit execution. +This module contains an exploratory representation of real-time operations on classical values +during circuit execution. Currently, only simple expressions on bits and registers that result in a Boolean value are supported, and these are only valid for use in the conditions of :meth:`.QuantumCircuit.if_test` diff --git a/qiskit/circuit/classical/expr/__init__.py b/qiskit/circuit/classical/expr/__init__.py index 4502aa52779a..9884062c5f51 100644 --- a/qiskit/circuit/classical/expr/__init__.py +++ b/qiskit/circuit/classical/expr/__init__.py @@ -39,8 +39,8 @@ These objects are mutable and should not be reused in a different location without a copy. -The base for dynamic variables is the :class:`Var`, which can be either an arbitrarily typed runtime -variable, or a wrapper around a :class:`.Clbit` or :class:`.ClassicalRegister`. +The base for dynamic variables is the :class:`Var`, which can be either an arbitrarily typed +real-time variable, or a wrapper around a :class:`.Clbit` or :class:`.ClassicalRegister`. .. autoclass:: Var :members: var, name diff --git a/qiskit/circuit/controlflow/break_loop.py b/qiskit/circuit/controlflow/break_loop.py index 45b61d5f0788..28863e3841bd 100644 --- a/qiskit/circuit/controlflow/break_loop.py +++ b/qiskit/circuit/controlflow/break_loop.py @@ -19,31 +19,17 @@ class BreakLoopOp(Instruction): - """A circuit operation which, when encountered, jumps to the end of - the nearest enclosing loop. - - .. note: - - Can be inserted only within the body of a loop op, and must span - the full width of that block. - - **Circuit symbol:** - - .. parsed-literal:: - - ┌──────────────┐ - q_0: ┤0 ├ - │ │ - q_1: ┤1 ├ - │ break_loop │ - q_2: ┤2 ├ - │ │ - c_0: ╡0 ╞ - └──────────────┘ - + """A circuit operation which, when encountered, jumps to the end of the nearest enclosing loop. + Can only be used inside loops. """ def __init__(self, num_qubits: int, num_clbits: int, label: Optional[str] = None): + """ + Args: + num_qubits: the number of qubits this affects. + num_clbits: the number of qubits this affects. + label: an optional string label for the instruction. + """ super().__init__("break_loop", num_qubits, num_clbits, [], label=label) diff --git a/qiskit/circuit/controlflow/builder.py b/qiskit/circuit/controlflow/builder.py index 38530c0dd8f3..c88e4c442e71 100644 --- a/qiskit/circuit/controlflow/builder.py +++ b/qiskit/circuit/controlflow/builder.py @@ -133,7 +133,7 @@ def remove_var(self, var: expr.Var): @abc.abstractmethod def use_var(self, var: expr.Var): - """Called for every standalone classical runtime variable being used by some circuit + """Called for every standalone classical real-time variable being used by some circuit instruction. The given variable is guaranteed to be a stand-alone variable; bit-like resource-wrapping diff --git a/qiskit/circuit/controlflow/continue_loop.py b/qiskit/circuit/controlflow/continue_loop.py index 8d00e23c03a9..ea8f414d28f8 100644 --- a/qiskit/circuit/controlflow/continue_loop.py +++ b/qiskit/circuit/controlflow/continue_loop.py @@ -19,31 +19,17 @@ class ContinueLoopOp(Instruction): - """A circuit operation which, when encountered, moves to the next iteration of - the nearest enclosing loop. - - .. note:: - - Can be inserted only within the body of a loop op, and must span the full - width of that block. - - **Circuit symbol:** - - .. parsed-literal:: - - ┌─────────────────┐ - q_0: ┤0 ├ - │ │ - q_1: ┤1 ├ - │ continue_loop │ - q_2: ┤2 ├ - │ │ - c_0: ╡0 ╞ - └─────────────────┘ - + """A circuit operation which, when encountered, moves to the next iteration of the nearest + enclosing loop. Can only be used inside loops. """ def __init__(self, num_qubits: int, num_clbits: int, label: Optional[str] = None): + """ + Args: + num_qubits: the number of qubits this affects. + num_clbits: the number of qubits this affects. + label: an optional string label for the instruction. + """ super().__init__("continue_loop", num_qubits, num_clbits, [], label=label) diff --git a/qiskit/circuit/controlflow/control_flow.py b/qiskit/circuit/controlflow/control_flow.py index fefa27efa27f..51b3709db6b5 100644 --- a/qiskit/circuit/controlflow/control_flow.py +++ b/qiskit/circuit/controlflow/control_flow.py @@ -25,7 +25,12 @@ class ControlFlowOp(Instruction, ABC): - """Abstract class to encapsulate all control flow operations.""" + """Abstract class to encapsulate all control flow operations. + + All subclasses of :class:`ControlFlowOp` have an internal attribute, + :attr:`~ControlFlowOp.blocks`, which exposes the inner subcircuits used in the different blocks + of the control flow. + """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -36,17 +41,34 @@ def __init__(self, *args, **kwargs): @property @abstractmethod def blocks(self) -> tuple[QuantumCircuit, ...]: - """Tuple of QuantumCircuits which may be executed as part of the - execution of this ControlFlowOp. May be parameterized by a loop - parameter to be resolved at run time. - """ + """Tuple of :class:`.QuantumCircuit`\\ s which may be executed as part of the + execution of this :class:`ControlFlowOp`.""" @abstractmethod def replace_blocks(self, blocks: typing.Iterable[QuantumCircuit]) -> ControlFlowOp: - """Replace blocks and return new instruction. + """Return a new version of this control-flow operations with the :attr:`blocks` mapped to + the given new ones. + + Typically this is used in a workflow such as:: + + existing_op = ... + + def map_block(block: QuantumCircuit) -> QuantumCircuit: + new_block = block.copy_empty_like() + # ... do something to `new_block` ... + return new_block + + new_op = existing_op.replace_blocks( + map_block(block) for block in existing_op.blocks + ) + + It is the caller's responsibility to ensure that the mapped blocks are defined over a + unified set of circuit resources, much like constructing a :class:`ControlFlowOp` using its + default constructor. + Args: - blocks: Tuple of QuantumCircuits to replace in instruction. + blocks: the new subcircuit blocks to use. Returns: - New ControlFlowOp with replaced blocks. + New :class:`ControlFlowOp` with replaced blocks. """ diff --git a/qiskit/circuit/controlflow/for_loop.py b/qiskit/circuit/controlflow/for_loop.py index 69c01aff8376..5584bf8cacde 100644 --- a/qiskit/circuit/controlflow/for_loop.py +++ b/qiskit/circuit/controlflow/for_loop.py @@ -29,28 +29,6 @@ class ForLoopOp(ControlFlowOp): """A circuit operation which repeatedly executes a subcircuit (``body``) parameterized by a parameter ``loop_parameter`` through the set of integer values provided in ``indexset``. - - Parameters: - indexset: A collection of integers to loop over. - loop_parameter: The placeholder parameterizing ``body`` to which - the values from ``indexset`` will be assigned. - body: The loop body to be repeatedly executed. - label: An optional label for identifying the instruction. - - **Circuit symbol:** - - .. parsed-literal:: - - ┌───────────┐ - q_0: ┤0 ├ - │ │ - q_1: ┤1 ├ - │ for_loop │ - q_2: ┤2 ├ - │ │ - c_0: ╡0 ╞ - └───────────┘ - """ def __init__( @@ -60,9 +38,16 @@ def __init__( body: QuantumCircuit, label: Optional[str] = None, ): + """ + Args: + indexset: A collection of integers to loop over. + loop_parameter: The placeholder parameterizing ``body`` to which + the values from ``indexset`` will be assigned. + body: The loop body to be repeatedly executed. + label: An optional label for identifying the instruction. + """ num_qubits = body.num_qubits num_clbits = body.num_clbits - super().__init__( "for_loop", num_qubits, num_clbits, [indexset, loop_parameter, body], label=label ) diff --git a/qiskit/circuit/controlflow/if_else.py b/qiskit/circuit/controlflow/if_else.py index 067c13bb7914..121d4c681f27 100644 --- a/qiskit/circuit/controlflow/if_else.py +++ b/qiskit/circuit/controlflow/if_else.py @@ -44,38 +44,11 @@ class IfElseOp(ControlFlowOp): provided condition (``condition``) evaluates to true, and optionally evaluates another program (``false_body``) otherwise. - Parameters: - condition: A condition to be evaluated at circuit runtime which, - if true, will trigger the evaluation of ``true_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``. - true_body: A program to be executed if ``condition`` evaluates - to true. - false_body: A optional program to be executed if ``condition`` - evaluates to false. - label: An optional label for identifying the instruction. - If provided, ``false_body`` must be of the same ``num_qubits`` and ``num_clbits`` as ``true_body``. The classical bits used in ``condition`` must be a subset of those attached to the circuit on which this ``IfElseOp`` will be appended. - - **Circuit symbol:** - - .. parsed-literal:: - - ┌───────────┐ - q_0: ┤0 ├ - │ │ - q_1: ┤1 ├ - │ if_else │ - q_2: ┤2 ├ - │ │ - c_0: ╡0 ╞ - └───────────┘ - """ def __init__( @@ -85,6 +58,19 @@ def __init__( false_body: QuantumCircuit | None = None, label: str | None = None, ): + """ + Args: + condition: A condition to be evaluated in real time during circuit execution which, + if true, will trigger the evaluation of ``true_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``. + true_body: A program to be executed if ``condition`` evaluates + to true. + false_body: A optional program to be executed if ``condition`` + evaluates to false. + label: An optional label for identifying the instruction. + """ # pylint: disable=cyclic-import from qiskit.circuit import QuantumCircuit diff --git a/qiskit/circuit/controlflow/switch_case.py b/qiskit/circuit/controlflow/switch_case.py index 0087d1efa083..446230c3c3cd 100644 --- a/qiskit/circuit/controlflow/switch_case.py +++ b/qiskit/circuit/controlflow/switch_case.py @@ -32,39 +32,25 @@ class _DefaultCaseType: - """The type of the default-case singleton. This is used instead of just having - ``CASE_DEFAULT = object()`` so we can set the pretty-printing properties, which are class-level - only.""" + # Note: Sphinx uses the docstring of this singleton class object as the documentation of the + # `CASE_DEFAULT` object. + + """A special object that represents the "default" case of a switch statement. If you use this + as a case target, it must be the last case, and will match anything that wasn't already matched. + When using the builder interface of :meth:`.QuantumCircuit.switch`, this can also be accessed as + the ``DEFAULT`` attribute of the bound case-builder object.""" def __repr__(self): return "" CASE_DEFAULT = _DefaultCaseType() -"""A special object that represents the "default" case of a switch statement. If you use this -as a case target, it must be the last case, and will match anything that wasn't already matched. -When using the builder interface of :meth:`.QuantumCircuit.switch`, this can also be accessed as the -``DEFAULT`` attribute of the bound case-builder object.""" class SwitchCaseOp(ControlFlowOp): """A circuit operation that executes one particular circuit block based on matching a given ``target`` against an ordered list of ``values``. The special value :data:`.CASE_DEFAULT` can be used to represent a default condition. - - This is the low-level interface for creating a switch-case statement; in general, the circuit - method :meth:`.QuantumCircuit.switch` should be used as a context manager to access the - builder interface. At the low level, you must ensure that all the circuit blocks contain equal - numbers of qubits and clbits, and that the order the virtual bits of the containing circuit - should be bound is the same for all blocks. This will likely mean that each circuit block is - wider than its natural width, as each block must span the union of all the spaces covered by - *any* of the blocks. - - Args: - target: the runtime value to switch on. - cases: an ordered iterable of the corresponding value of the ``target`` and the circuit - block that should be executed if this is matched. There is no fall-through between - blocks, and the order matters. """ def __init__( @@ -74,6 +60,13 @@ def __init__( *, label: Optional[str] = None, ): + """ + Args: + target: the real-time value to switch on. + cases: an ordered iterable of the corresponding value of the ``target`` and the circuit + block that should be executed if this is matched. There is no fall-through between + blocks, and the order matters. + """ # pylint: disable=cyclic-import from qiskit.circuit import QuantumCircuit diff --git a/qiskit/circuit/controlflow/while_loop.py b/qiskit/circuit/controlflow/while_loop.py index e834b4718cee..488a7a8b7025 100644 --- a/qiskit/circuit/controlflow/while_loop.py +++ b/qiskit/circuit/controlflow/while_loop.py @@ -30,31 +30,8 @@ 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__( @@ -63,6 +40,15 @@ def __init__( body: QuantumCircuit, label: str | None = None, ): + """ + Args: + 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. + """ num_qubits = body.num_qubits num_clbits = body.num_clbits diff --git a/qiskit/circuit/controlledgate.py b/qiskit/circuit/controlledgate.py index 8fc08a076b4c..a2038be9bb0c 100644 --- a/qiskit/circuit/controlledgate.py +++ b/qiskit/circuit/controlledgate.py @@ -111,9 +111,9 @@ def __init__( @property def definition(self) -> QuantumCircuit: """Return definition in terms of other basic gates. If the gate has - open controls, as determined from `self.ctrl_state`, the returned + open controls, as determined from :attr:`ctrl_state`, the returned definition is conjugated with X without changing the internal - `_definition`. + ``_definition``. """ if self._open_ctrl: closed_gate = self.to_mutable() diff --git a/qiskit/circuit/delay.py b/qiskit/circuit/delay.py index 8e3ce5618174..16d84d15cbe5 100644 --- a/qiskit/circuit/delay.py +++ b/qiskit/circuit/delay.py @@ -24,7 +24,11 @@ class Delay(Instruction): """Do nothing and just delay/wait/idle for a specified duration.""" def __init__(self, duration, unit="dt"): - """Create new delay instruction.""" + """ + Args: + duration: the length of time of the duration. Given in units of ``unit``. + unit: the unit of the duration. Must be ``"dt"`` or an SI-prefixed seconds unit. + """ if unit not in {"s", "ms", "us", "ns", "ps", "dt"}: raise CircuitError("Unknown unit %s is specified." % unit) diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py index 3653a03d01d5..e339cb8d94bb 100644 --- a/qiskit/circuit/instruction.py +++ b/qiskit/circuit/instruction.py @@ -268,12 +268,17 @@ def soft_compare(self, other: "Instruction") -> bool: return True def _define(self): - """Populates self.definition with a decomposition of this gate.""" + """Populate the cached :attr:`_definition` field of this :class:`Instruction`. + + Subclasses should implement this method to provide lazy construction of their public + :attr:`definition` attribute. A subclass can use its :attr:`params` at the time of the + call. The method should populate :attr:`_definition` with a :class:`.QuantumCircuit` and + not return a value.""" pass @property def params(self): - """return instruction params.""" + """The parameters of this :class:`Instruction`. Ideally these will be gate angles.""" return self._params @params.setter @@ -290,7 +295,8 @@ def validate_parameter(self, parameter): return parameter def is_parameterized(self): - """Return True .IFF. instruction is parameterized else False""" + """Return whether the :class:`Instruction` contains :ref:`compile-time parameters + `.""" return any( isinstance(param, ParameterExpression) and param.parameters for param in self.params ) diff --git a/qiskit/circuit/instructionset.py b/qiskit/circuit/instructionset.py index dcf1746486ee..ac3d9fabd64b 100644 --- a/qiskit/circuit/instructionset.py +++ b/qiskit/circuit/instructionset.py @@ -87,7 +87,11 @@ def _add_ref(self, data: MutableSequence[CircuitInstruction], pos: int): self._instructions.append((data, pos)) def inverse(self, annotated: bool = False): - """Invert all instructions.""" + """Invert all instructions. + + .. note:: + It is preferable to take the inverse *before* appending the gate(s) to the circuit. + """ for i, instruction in enumerate(self._instructions): if isinstance(instruction, CircuitInstruction): self._instructions[i] = instruction.replace( @@ -105,6 +109,10 @@ def c_if(self, classical: Clbit | ClassicalRegister | int, val: int) -> "Instruc """Set a classical equality condition on all the instructions in this set between the :obj:`.ClassicalRegister` or :obj:`.Clbit` ``classical`` and value ``val``. + .. note:: + You should prefer to use the :meth:`.QuantumCircuit.if_test` builder interface, rather + than using this method. + .. note:: This is a setter method, not an additive one. Calling this multiple times will silently @@ -124,27 +132,6 @@ def c_if(self, classical: Clbit | ClassicalRegister | int, val: int) -> "Instruc Raises: CircuitError: if the passed classical resource is invalid, or otherwise not resolvable to a concrete resource that these instructions are permitted to access. - - Example: - .. plot:: - :include-source: - - from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit - - qr = QuantumRegister(2) - cr = ClassicalRegister(2) - qc = QuantumCircuit(qr, cr) - qc.h(range(2)) - qc.measure(range(2), range(2)) - - # apply x gate if the classical register has the value 2 (10 in binary) - qc.x(0).c_if(cr, 2) - - # apply y gate if bit 0 is set to 1 - qc.y(1).c_if(0, 1) - - qc.draw('mpl') - """ if self._requester is None and not isinstance(classical, (Clbit, ClassicalRegister)): raise CircuitError( diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index 710b9533d9cd..5f21967e4828 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -137,8 +137,11 @@ the transpiler. In general, the transpiler or backend might optionally ignore them if there is no implementation for them. +.. + This summary table deliberately does not generate toctree entries; these directives are "owned" + by ``qiskit.circuit``. + .. autosummary:: - :toctree: ../stubs/ Barrier @@ -147,8 +150,11 @@ Operations are non-reversible changes in the quantum state of the circuit. +.. + This summary table deliberately does not generate toctree entries; these directives are "owned" + by ``qiskit.circuit``. + .. autosummary:: - :toctree: ../stubs/ Measure Reset diff --git a/qiskit/circuit/library/arithmetic/integer_comparator.py b/qiskit/circuit/library/arithmetic/integer_comparator.py index 1324d512ea6f..377af5f222e0 100644 --- a/qiskit/circuit/library/arithmetic/integer_comparator.py +++ b/qiskit/circuit/library/arithmetic/integer_comparator.py @@ -14,7 +14,7 @@ """Integer Comparator.""" from __future__ import annotations -import numpy as np +import math from qiskit.circuit import QuantumCircuit, QuantumRegister, AncillaRegister from qiskit.circuit.exceptions import CircuitError @@ -140,7 +140,7 @@ def _get_twos_complement(self) -> list[int]: Returns: The 2's complement of ``self.value``. """ - twos_complement = pow(2, self.num_state_qubits) - int(np.ceil(self.value)) + twos_complement = pow(2, self.num_state_qubits) - math.ceil(self.value) twos_complement = f"{twos_complement:b}".rjust(self.num_state_qubits, "0") twos_complement = [ 1 if twos_complement[i] == "1" else 0 for i in reversed(range(len(twos_complement))) diff --git a/qiskit/circuit/library/arithmetic/quadratic_form.py b/qiskit/circuit/library/arithmetic/quadratic_form.py index bd0bca1664a3..5d6fd3218265 100644 --- a/qiskit/circuit/library/arithmetic/quadratic_form.py +++ b/qiskit/circuit/library/arithmetic/quadratic_form.py @@ -13,6 +13,7 @@ """A circuit implementing a quadratic form on binary variables.""" from typing import Union, Optional, List +import math import numpy as np @@ -190,8 +191,8 @@ def required_result_qubits( # the minimum number of qubits is the number of qubits needed to represent # the minimum/maximum value plus one sign qubit - num_qubits_for_min = int(np.ceil(np.log2(max(-bounds[0], 1)))) - num_qubits_for_max = int(np.ceil(np.log2(bounds[1] + 1))) + num_qubits_for_min = math.ceil(math.log2(max(-bounds[0], 1))) + num_qubits_for_max = math.ceil(math.log2(bounds[1] + 1)) num_result_qubits = 1 + max(num_qubits_for_min, num_qubits_for_max) return num_result_qubits diff --git a/qiskit/circuit/library/data_preparation/state_preparation.py b/qiskit/circuit/library/data_preparation/state_preparation.py index 95f6d6fa538a..2d48e5cd0776 100644 --- a/qiskit/circuit/library/data_preparation/state_preparation.py +++ b/qiskit/circuit/library/data_preparation/state_preparation.py @@ -11,6 +11,7 @@ # that they have been altered from the originals. """Prepare a quantum state from the state where all qubits are 0.""" +import cmath from typing import Union, Optional import math @@ -339,20 +340,20 @@ def _bloch_angles(pair_of_complex): a_complex = complex(a_complex) b_complex = complex(b_complex) mag_a = abs(a_complex) - final_r = np.sqrt(mag_a**2 + np.absolute(b_complex) ** 2) + final_r = math.sqrt(mag_a**2 + abs(b_complex) ** 2) if final_r < _EPS: theta = 0 phi = 0 final_r = 0 final_t = 0 else: - theta = 2 * np.arccos(mag_a / final_r) - a_arg = np.angle(a_complex) - b_arg = np.angle(b_complex) + theta = 2 * math.acos(mag_a / final_r) + a_arg = cmath.phase(a_complex) + b_arg = cmath.phase(b_complex) final_t = a_arg + b_arg phi = b_arg - a_arg - return final_r * np.exp(1.0j * final_t / 2), theta, phi + return final_r * cmath.exp(1.0j * final_t / 2), theta, phi def _multiplex(self, target_gate, list_of_angles, last_cnot=True): """ diff --git a/qiskit/circuit/library/generalized_gates/diagonal.py b/qiskit/circuit/library/generalized_gates/diagonal.py index 2653675b14a9..5ea4bfa0e813 100644 --- a/qiskit/circuit/library/generalized_gates/diagonal.py +++ b/qiskit/circuit/library/generalized_gates/diagonal.py @@ -17,6 +17,7 @@ from collections.abc import Sequence import cmath +import math import numpy as np from qiskit.circuit.gate import Gate @@ -87,7 +88,7 @@ def __init__(self, diag: Sequence[complex]) -> None: number of qubits. """ self._check_input(diag) - num_qubits = int(np.log2(len(diag))) + num_qubits = int(math.log2(len(diag))) circuit = QuantumCircuit(num_qubits, name="Diagonal") @@ -100,7 +101,7 @@ def __init__(self, diag: Sequence[complex]) -> None: for i in range(0, n, 2): diag_phases[i // 2], rz_angle = _extract_rz(diag_phases[i], diag_phases[i + 1]) angles_rz.append(rz_angle) - num_act_qubits = int(np.log2(n)) + num_act_qubits = int(math.log2(n)) ctrl_qubits = list(range(num_qubits - num_act_qubits + 1, num_qubits)) target_qubit = num_qubits - num_act_qubits @@ -118,7 +119,7 @@ def _check_input(diag): """Check if ``diag`` is in valid format.""" if not isinstance(diag, (list, np.ndarray)): raise CircuitError("Diagonal entries must be in a list or numpy array.") - num_qubits = np.log2(len(diag)) + num_qubits = math.log2(len(diag)) if num_qubits < 1 or not num_qubits.is_integer(): raise CircuitError("The number of diagonal entries is not a positive power of 2.") if not np.allclose(np.abs(diag), 1, atol=_EPS): @@ -134,7 +135,7 @@ def __init__(self, diag: Sequence[complex]) -> None: diag: list of the :math:`2^k` diagonal entries (for a diagonal gate on :math:`k` qubits). """ Diagonal._check_input(diag) - num_qubits = int(np.log2(len(diag))) + num_qubits = int(math.log2(len(diag))) super().__init__("diagonal", num_qubits, diag) diff --git a/qiskit/circuit/library/generalized_gates/isometry.py b/qiskit/circuit/library/generalized_gates/isometry.py index d536a38812e3..1294feb26342 100644 --- a/qiskit/circuit/library/generalized_gates/isometry.py +++ b/qiskit/circuit/library/generalized_gates/isometry.py @@ -22,6 +22,7 @@ from __future__ import annotations import itertools +import math import numpy as np from qiskit.circuit.exceptions import CircuitError from qiskit.circuit.instruction import Instruction @@ -93,8 +94,8 @@ def __init__( self._epsilon = epsilon # Check if the isometry has the right dimension and if the columns are orthonormal - n = np.log2(isometry.shape[0]) - m = np.log2(isometry.shape[1]) + n = math.log2(isometry.shape[0]) + m = math.log2(isometry.shape[1]) if not n.is_integer() or n < 0: raise QiskitError( "The number of rows of the isometry is not a non negative power of 2." @@ -150,7 +151,7 @@ def _gates_to_uncompute(self): # to keep a copyof the input isometry) remaining_isometry = self.iso_data.astype(complex) # note: "astype" does copy the isometry diag = [] - m = int(np.log2((self.iso_data).shape[1])) + m = int(math.log2(self.iso_data.shape[1])) # Decompose the column with index column_index and attache the gate to the circuit object. # Return the isometry that is left to decompose, where the columns up to index column_index # correspond to the firstfew columns of the identity matrix up to diag, and hence we only @@ -170,7 +171,7 @@ def _decompose_column(self, circuit, q, diag, remaining_isometry, column_index): """ Decomposes the column with index column_index. """ - n = int(np.log2(self.iso_data.shape[0])) + n = int(math.log2(self.iso_data.shape[0])) for s in range(n): self._disentangle(circuit, q, diag, remaining_isometry, column_index, s) @@ -185,7 +186,7 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): # (note that we remove columns of the isometry during the procedure for efficiency) k_prime = 0 v = remaining_isometry - n = int(np.log2(self.iso_data.shape[0])) + n = int(math.log2(self.iso_data.shape[0])) # MCG to set one entry to zero (preparation for disentangling with UCGate): index1 = 2 * _a(k, s + 1) * 2**s + _b(k, s + 1) @@ -237,7 +238,7 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): # The qubit with label n-s-1 is disentangled into the basis state k_s(k,s). def _find_squs_for_disentangling(self, v, k, s): k_prime = 0 - n = int(np.log2(self.iso_data.shape[0])) + n = int(math.log2(self.iso_data.shape[0])) if _b(k, s + 1) == 0: i_start = _a(k, s + 1) else: @@ -264,7 +265,7 @@ def _append_ucg_up_to_diagonal(self, circ, q, single_qubit_gates, control_labels q_ancillas_zero, q_ancillas_dirty, ) = self._define_qubit_role(q) - n = int(np.log2(self.iso_data.shape[0])) + n = int(math.log2(self.iso_data.shape[0])) qubits = q_input + q_ancillas_for_output # Note that we have to reverse the control labels, since controls are provided by # increasing qubit number toa UCGate by convention @@ -286,7 +287,7 @@ def _append_mcg_up_to_diagonal(self, circ, q, gate, control_labels, target_label q_ancillas_zero, q_ancillas_dirty, ) = self._define_qubit_role(q) - n = int(np.log2(self.iso_data.shape[0])) + n = int(math.log2(self.iso_data.shape[0])) qubits = q_input + q_ancillas_for_output control_qubits = _reverse_qubit_oder(_get_qubits_by_label(control_labels, qubits, n)) target_qubit = _get_qubits_by_label([target_label], qubits, n)[0] @@ -307,8 +308,8 @@ def _append_mcg_up_to_diagonal(self, circ, q, gate, control_labels, target_label def _define_qubit_role(self, q): - n = int(np.log2(self.iso_data.shape[0])) - m = int(np.log2(self.iso_data.shape[1])) + n = int(math.log2(self.iso_data.shape[0])) + m = int(math.log2(self.iso_data.shape[1])) # Define the role of the qubits q_input = q[:m] @@ -372,7 +373,7 @@ def _reverse_qubit_state(state, basis_state, epsilon): def _apply_ucg(m, k, single_qubit_gates): # ToDo: Improve efficiency by parallelizing the gate application. A generalized version of # ToDo: this method should be implemented by the state vector simulator in Qiskit AER. - num_qubits = int(np.log2(m.shape[0])) + num_qubits = int(math.log2(m.shape[0])) num_col = m.shape[1] spacing = 2 ** (num_qubits - k - 1) for j in range(2 ** (num_qubits - 1)): @@ -395,7 +396,7 @@ def _apply_ucg(m, k, single_qubit_gates): def _apply_diagonal_gate(m, action_qubit_labels, diag): # ToDo: Improve efficiency by parallelizing the gate application. A generalized version of # ToDo: this method should be implemented by the state vector simulator in Qiskit AER. - num_qubits = int(np.log2(m.shape[0])) + num_qubits = int(math.log2(m.shape[0])) num_cols = m.shape[1] basis_states = list(itertools.product([0, 1], repeat=num_qubits)) for state in basis_states: @@ -436,7 +437,7 @@ def _apply_diagonal_gate_to_diag(m_diagonal, action_qubit_labels, diag, num_qubi def _apply_multi_controlled_gate(m, control_labels, target_label, gate): # ToDo: This method should be integrated into the state vector simulator in Qiskit AER. - num_qubits = int(np.log2(m.shape[0])) + num_qubits = int(math.log2(m.shape[0])) num_cols = m.shape[1] control_labels.sort() free_qubits = num_qubits - len(control_labels) - 1 diff --git a/qiskit/circuit/library/generalized_gates/rv.py b/qiskit/circuit/library/generalized_gates/rv.py index 5f614e7ab346..6853d00b0fba 100644 --- a/qiskit/circuit/library/generalized_gates/rv.py +++ b/qiskit/circuit/library/generalized_gates/rv.py @@ -12,6 +12,7 @@ """Rotation around an arbitrary axis on the Bloch sphere.""" +import math import numpy from qiskit.circuit.gate import Gate from qiskit.circuit.exceptions import CircuitError @@ -82,12 +83,12 @@ def inverse(self, annotated: bool = False): def to_matrix(self): """Return a numpy.array for the R(v) gate.""" v = numpy.asarray(self.params, dtype=float) - angle = numpy.sqrt(v.dot(v)) + angle = math.sqrt(v.dot(v)) if angle == 0: return numpy.array([[1, 0], [0, 1]]) nx, ny, nz = v / angle - sin = numpy.sin(angle / 2) - cos = numpy.cos(angle / 2) + sin = math.sin(angle / 2) + cos = math.cos(angle / 2) return numpy.array( [ [cos - 1j * nz * sin, (-ny - 1j * nx) * sin], diff --git a/qiskit/circuit/library/generalized_gates/unitary.py b/qiskit/circuit/library/generalized_gates/unitary.py index d68e74f190a1..c065abbe7cd1 100644 --- a/qiskit/circuit/library/generalized_gates/unitary.py +++ b/qiskit/circuit/library/generalized_gates/unitary.py @@ -13,6 +13,7 @@ """Arbitrary unitary circuit instruction.""" from __future__ import annotations +import math import typing import numpy @@ -96,7 +97,7 @@ def __init__( # Convert to numpy array in case not already an array data = numpy.asarray(data, dtype=complex) input_dim, output_dim = data.shape - num_qubits = int(numpy.log2(input_dim)) + num_qubits = int(math.log2(input_dim)) if check_input: # Check input is unitary if not is_unitary_matrix(data): diff --git a/qiskit/circuit/library/hamiltonian_gate.py b/qiskit/circuit/library/hamiltonian_gate.py index 91950bd40795..a87504a97b36 100644 --- a/qiskit/circuit/library/hamiltonian_gate.py +++ b/qiskit/circuit/library/hamiltonian_gate.py @@ -15,6 +15,7 @@ """ from __future__ import annotations +import math import typing from numbers import Number @@ -67,7 +68,7 @@ def __init__( # numpy matrix from `Operator.data`. data = data.to_operator().data # Convert to np array in case not already an array - data = np.array(data, dtype=complex) + data = np.asarray(data, dtype=complex) # Check input is unitary if not is_hermitian_matrix(data): raise ValueError("Input matrix is not Hermitian.") @@ -75,7 +76,7 @@ def __init__( raise ValueError("Evolution time is not real.") # Check input is N-qubit matrix input_dim, output_dim = data.shape - num_qubits = int(np.log2(input_dim)) + num_qubits = int(math.log2(input_dim)) if input_dim != output_dim or 2**num_qubits != input_dim: raise ValueError("Input matrix is not an N-qubit operator.") diff --git a/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py b/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py index bd062be23752..16907949456a 100644 --- a/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +++ b/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py @@ -14,6 +14,7 @@ """ from math import pi +import math from typing import Optional, Union, Tuple, List import numpy as np @@ -144,18 +145,20 @@ def _mcsu2_real_diagonal( if np.isclose(z, -1): s_op = [[1.0, 0.0], [0.0, 1.0j]] else: - alpha_r = np.sqrt((np.sqrt((z.real + 1.0) / 2.0) + 1.0) / 2.0) - alpha_i = z.imag / (2.0 * np.sqrt((z.real + 1.0) * (np.sqrt((z.real + 1.0) / 2.0) + 1.0))) + alpha_r = math.sqrt((math.sqrt((z.real + 1.0) / 2.0) + 1.0) / 2.0) + alpha_i = z.imag / ( + 2.0 * math.sqrt((z.real + 1.0) * (math.sqrt((z.real + 1.0) / 2.0) + 1.0)) + ) alpha = alpha_r + 1.0j * alpha_i - beta = x / (2.0 * np.sqrt((z.real + 1.0) * (np.sqrt((z.real + 1.0) / 2.0) + 1.0))) + beta = x / (2.0 * math.sqrt((z.real + 1.0) * (math.sqrt((z.real + 1.0) / 2.0) + 1.0))) # S gate definition s_op = np.array([[alpha, -np.conj(beta)], [beta, np.conj(alpha)]]) s_gate = UnitaryGate(s_op) - k_1 = int(np.ceil(num_controls / 2.0)) - k_2 = int(np.floor(num_controls / 2.0)) + k_1 = math.ceil(num_controls / 2.0) + k_2 = math.floor(num_controls / 2.0) ctrl_state_k_1 = None ctrl_state_k_2 = None diff --git a/qiskit/circuit/library/standard_gates/u.py b/qiskit/circuit/library/standard_gates/u.py index 53406644fef6..81b48536f26b 100644 --- a/qiskit/circuit/library/standard_gates/u.py +++ b/qiskit/circuit/library/standard_gates/u.py @@ -11,6 +11,7 @@ # that they have been altered from the originals. """Two-pulse single-qubit gate.""" +import cmath import copy import math from cmath import exp @@ -339,12 +340,12 @@ def inverse(self, annotated: bool = False): def __array__(self, dtype=None): """Return a numpy.array for the CU gate.""" theta, phi, lam, gamma = (float(param) for param in self.params) - cos = numpy.cos(theta / 2) - sin = numpy.sin(theta / 2) - a = numpy.exp(1j * gamma) * cos - b = -numpy.exp(1j * (gamma + lam)) * sin - c = numpy.exp(1j * (gamma + phi)) * sin - d = numpy.exp(1j * (gamma + phi + lam)) * cos + cos = math.cos(theta / 2) + sin = math.sin(theta / 2) + a = cmath.exp(1j * gamma) * cos + b = -cmath.exp(1j * (gamma + lam)) * sin + c = cmath.exp(1j * (gamma + phi)) * sin + d = cmath.exp(1j * (gamma + phi + lam)) * cos if self.ctrl_state: return numpy.array( [[1, 0, 0, 0], [0, a, 0, b], [0, 0, 1, 0], [0, c, 0, d]], dtype=dtype diff --git a/qiskit/circuit/measure.py b/qiskit/circuit/measure.py index 548aaecd81a5..ab329903db4a 100644 --- a/qiskit/circuit/measure.py +++ b/qiskit/circuit/measure.py @@ -22,7 +22,10 @@ class Measure(SingletonInstruction): """Quantum measurement in the computational basis.""" def __init__(self, label=None, *, duration=None, unit="dt"): - """Create new measurement instruction.""" + """ + Args: + label: optional string label for this instruction. + """ super().__init__("measure", 1, 1, [], label=label, duration=duration, unit=unit) _singleton_lookup_key = stdlib_singleton_key() diff --git a/qiskit/circuit/operation.py b/qiskit/circuit/operation.py index d4d2a719a9b8..c299e130178a 100644 --- a/qiskit/circuit/operation.py +++ b/qiskit/circuit/operation.py @@ -16,17 +16,22 @@ class Operation(ABC): - """Quantum Operation Interface Class. - For objects that can be added to a :class:`~qiskit.circuit.QuantumCircuit`. - These objects include :class:`~qiskit.circuit.Gate`, :class:`~qiskit.circuit.Reset`, - :class:`~qiskit.circuit.Barrier`, :class:`~qiskit.circuit.Measure`, - and operators such as :class:`~qiskit.quantum_info.Clifford`. - The main purpose is to add an :class:`~qiskit.circuit.Operation` to a - :class:`~qiskit.circuit.QuantumCircuit` without synthesizing it before the transpilation. + """Quantum operation interface. + + The minimal interface that any object must fulfil in order to be added to a + :class:`.QuantumCircuit`. + + Concrete instances of this interface include :class:`~qiskit.circuit.Gate`, + :class:`~qiskit.circuit.Reset`, :class:`~qiskit.circuit.Barrier`, + :class:`~qiskit.circuit.Measure`, and operators such as :class:`~qiskit.quantum_info.Clifford`. + + The main purpose is to add allow abstract mathematical objects to be added directly onto + abstract circuits, and for the exact syntheses of these to be determined later, during + compilation. Example: - Add a Clifford and a Toffoli gate to a QuantumCircuit. + Add a Clifford and a Toffoli gate to a :class:`QuantumCircuit`. .. plot:: :include-source: diff --git a/qiskit/circuit/parameter.py b/qiskit/circuit/parameter.py index 69a00fd166de..4d0f73cf0772 100644 --- a/qiskit/circuit/parameter.py +++ b/qiskit/circuit/parameter.py @@ -25,10 +25,16 @@ class Parameter(ParameterExpression): - """Parameter Class for variable parameters. + """A compile-time symbolic parameter. - A parameter is a variable value that is not required to be fixed - at circuit definition. + The value of a :class:`Parameter` must be entirely determined before a circuit begins execution. + Typically this will mean that you should supply values for all :class:`Parameter`\\ s in a + circuit using :meth:`.QuantumCircuit.assign_parameters`, though certain hardware vendors may + allow you to give them a circuit in terms of these parameters, provided you also pass the values + separately. + + This is the atom of :class:`.ParameterExpression`, and is itself an expression. The numeric + value of a parameter need not be fixed while the circuit is being defined. Examples: @@ -62,11 +68,10 @@ class Parameter(ParameterExpression): def __init__( self, name: str, *, uuid: UUID | None = None ): # pylint: disable=super-init-not-called - """Create a new named :class:`Parameter`. - + """ Args: name: name of the ``Parameter``, used for visual representation. This can - be any unicode string, e.g. "ϕ". + be any Unicode string, e.g. "ϕ". uuid: For advanced usage only. Override the UUID of this parameter, in order to make it compare equal to some other parameter object. By default, two parameters with the same name do not compare equal to help catch shadowing bugs when two circuits diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index b7d9377e1161..ed170dde6cb2 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -144,7 +144,7 @@ class QuantumCircuit: 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 + 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 @@ -1171,14 +1171,14 @@ def ancillas(self) -> list[AncillaQubit]: @property def num_vars(self) -> int: - """The number of runtime classical variables in the circuit. + """The number of real-time classical variables in the circuit. This is the length of the :meth:`iter_vars` iterable.""" return self.num_input_vars + self.num_captured_vars + self.num_declared_vars @property def num_input_vars(self) -> int: - """The number of runtime classical variables in the circuit marked as circuit inputs. + """The number of real-time classical variables in the circuit marked as circuit inputs. This is the length of the :meth:`iter_input_vars` iterable. If this is non-zero, :attr:`num_captured_vars` must be zero.""" @@ -1186,7 +1186,7 @@ def num_input_vars(self) -> int: @property def num_captured_vars(self) -> int: - """The number of runtime classical variables in the circuit marked as captured from an + """The number of real-time classical variables in the circuit marked as captured from an enclosing scope. This is the length of the :meth:`iter_captured_vars` iterable. If this is non-zero, @@ -1195,14 +1195,14 @@ def num_captured_vars(self) -> int: @property def num_declared_vars(self) -> int: - """The number of runtime classical variables in the circuit that are declared by this + """The number of real-time classical variables in the circuit that are declared by this circuit scope, excluding inputs or captures. This is the length of the :meth:`iter_declared_vars` iterable.""" return len(self._vars_local) def iter_vars(self) -> typing.Iterable[expr.Var]: - """Get an iterable over all runtime classical variables in scope within this circuit. + """Get an iterable over all real-time classical variables in scope within this circuit. This method will iterate over all variables in scope. For more fine-grained iterators, see :meth:`iter_declared_vars`, :meth:`iter_input_vars` and :meth:`iter_captured_vars`.""" @@ -1214,7 +1214,7 @@ def iter_vars(self) -> typing.Iterable[expr.Var]: ) def iter_declared_vars(self) -> typing.Iterable[expr.Var]: - """Get an iterable over all runtime classical variables that are declared with automatic + """Get an iterable over all real-time classical variables that are declared with automatic storage duration in this scope. This excludes input variables (see :meth:`iter_input_vars`) and captured variables (see :meth:`iter_captured_vars`).""" if self._control_flow_scopes: @@ -1222,15 +1222,15 @@ def iter_declared_vars(self) -> typing.Iterable[expr.Var]: return self._vars_local.values() def iter_input_vars(self) -> typing.Iterable[expr.Var]: - """Get an iterable over all runtime classical variables that are declared as inputs to this - circuit scope. This excludes locally declared variables (see :meth:`iter_declared_vars`) - and captured variables (see :meth:`iter_captured_vars`).""" + """Get an iterable over all real-time classical variables that are declared as inputs to + this circuit scope. This excludes locally declared variables (see + :meth:`iter_declared_vars`) and captured variables (see :meth:`iter_captured_vars`).""" if self._control_flow_scopes: return () return self._vars_input.values() def iter_captured_vars(self) -> typing.Iterable[expr.Var]: - """Get an iterable over all runtime classical variables that are captured by this circuit + """Get an iterable over all real-time classical variables that are captured by this circuit scope from a containing scope. This excludes input variables (see :meth:`iter_input_vars`) and locally declared variables (see :meth:`iter_declared_vars`).""" if self._control_flow_scopes: @@ -1730,8 +1730,8 @@ def add_var(self, name_or_var: str | expr.Var, /, initial: typing.Any) -> expr.V qc.cx(0, i) qc.measure(range(8), cr2) - # Now when we add the variable, it is initialized using the runtime state of the two - # classical registers we measured into above. + # Now when we add the variable, it is initialized using the real-time state of the + # two classical registers we measured into above. qc.add_var(my_var, expr.bit_and(cr1, cr2)) """ # Validate the initialiser first to catch cases where the variable to be declared is being @@ -2619,7 +2619,7 @@ def reset(self, qubit: QubitSpecifier) -> InstructionSet: return self.append(Reset(), [qubit], []) def store(self, lvalue: typing.Any, rvalue: typing.Any, /) -> InstructionSet: - """Store the result of the given runtime classical expression ``rvalue`` in the memory + """Store the result of the given real-time classical expression ``rvalue`` in the memory location defined by ``lvalue``. Typically ``lvalue`` will be a :class:`~.expr.Var` node and ``rvalue`` will be some @@ -2631,7 +2631,7 @@ def store(self, lvalue: typing.Any, rvalue: typing.Any, /) -> InstructionSet: a :class:`~.expr.Var` node, but you can also write to :class:`.Clbit` or :class:`.ClassicalRegister` memory locations if your hardware supports it. The memory location must already be present in the circuit. - rvalue: a runtime classical expression whose result should be written into the given + rvalue: a real-time classical expression whose result should be written into the given memory location. .. seealso:: @@ -5023,11 +5023,11 @@ def if_test( qc.z(2) Args: - condition (Tuple[Union[ClassicalRegister, Clbit], int]): A condition to be evaluated at - circuit runtime which, if true, will trigger the evaluation of ``true_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``. + condition (Tuple[Union[ClassicalRegister, Clbit], int]): A condition to be evaluated in + real time during circuit execution, which, if true, will trigger the evaluation of + ``true_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``. true_body (Optional[QuantumCircuit]): The circuit body to be run if ``condition`` is true. qubits (Optional[Sequence[QubitSpecifier]]): The circuit qubits over which the if/else @@ -5099,7 +5099,7 @@ def if_else( qc.x(0) Args: - condition: A condition to be evaluated at circuit runtime which, + condition: A condition to be evaluated in real time at circuit execution, which, if true, will trigger the evaluation of ``true_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 diff --git a/qiskit/circuit/reset.py b/qiskit/circuit/reset.py index 183004dbf877..3649a882655e 100644 --- a/qiskit/circuit/reset.py +++ b/qiskit/circuit/reset.py @@ -18,10 +18,13 @@ class Reset(SingletonInstruction): - """Qubit reset.""" + r"""Incoherently reset a qubit to the :math:`\lvert0\rangle` state.""" def __init__(self, label=None, *, duration=None, unit="dt"): - """Create new reset instruction.""" + """ + Args: + label: optional string label of this instruction. + """ super().__init__("reset", 1, 0, [], label=label, duration=duration, unit=unit) _singleton_lookup_key = stdlib_singleton_key() diff --git a/qiskit/circuit/store.py b/qiskit/circuit/store.py index 100fe0e629b9..857cb4f6c2d0 100644 --- a/qiskit/circuit/store.py +++ b/qiskit/circuit/store.py @@ -57,10 +57,14 @@ class Store(Instruction): This is a low-level primitive of the classical-expression handling (similar to how :class:`~.circuit.Measure` is a primitive for quantum measurement), and is not safe for - subclassing. It is likely to become a special-case instruction in later versions of Qiskit - circuit and compiler internal representations.""" + subclassing.""" def __init__(self, lvalue: expr.Expr, rvalue: expr.Expr): + """ + Args: + lvalue: the memory location being stored into. + rvalue: the expression result being stored. + """ if not expr.is_lvalue(lvalue): raise CircuitError(f"'{lvalue}' is not an l-value") @@ -82,6 +86,7 @@ def rvalue(self): return self.params[1] def c_if(self, classical, val): + """:meta hidden:""" raise NotImplementedError( "stores cannot be conditioned with `c_if`; use a full `if_test` context instead" ) diff --git a/qiskit/primitives/backend_estimator_v2.py b/qiskit/primitives/backend_estimator_v2.py index 91e45a16e989..525b223b9505 100644 --- a/qiskit/primitives/backend_estimator_v2.py +++ b/qiskit/primitives/backend_estimator_v2.py @@ -17,6 +17,7 @@ from collections import defaultdict from collections.abc import Iterable from dataclasses import dataclass +import math import numpy as np @@ -146,7 +147,7 @@ def _run(self, pubs: list[EstimatorPub]) -> PrimitiveResult[PubResult]: return PrimitiveResult([self._run_pub(pub) for pub in pubs]) def _run_pub(self, pub: EstimatorPub) -> PubResult: - shots = int(np.ceil(1.0 / pub.precision**2)) + shots = math.ceil(1.0 / pub.precision**2) circuit = pub.circuit observables = pub.observables parameter_values = pub.parameter_values diff --git a/qiskit/providers/basic_provider/basic_simulator.py b/qiskit/providers/basic_provider/basic_simulator.py index 1042a91e5ac7..e19021519194 100644 --- a/qiskit/providers/basic_provider/basic_simulator.py +++ b/qiskit/providers/basic_provider/basic_simulator.py @@ -29,6 +29,7 @@ from __future__ import annotations +import math import uuid import time import logging @@ -331,9 +332,9 @@ def _add_measure(self, qubit: int, cmembit: int, cregbit: int | None = None) -> # update quantum state if outcome == "0": - update_diag = [[1 / np.sqrt(probability), 0], [0, 0]] + update_diag = [[1 / math.sqrt(probability), 0], [0, 0]] else: - update_diag = [[0, 0], [0, 1 / np.sqrt(probability)]] + update_diag = [[0, 0], [0, 1 / math.sqrt(probability)]] # update classical state self._add_unitary(update_diag, [qubit]) @@ -351,10 +352,10 @@ def _add_reset(self, qubit: int) -> None: outcome, probability = self._get_measure_outcome(qubit) # update quantum state if outcome == "0": - update = [[1 / np.sqrt(probability), 0], [0, 0]] + update = [[1 / math.sqrt(probability), 0], [0, 0]] self._add_unitary(update, [qubit]) else: - update = [[0, 1 / np.sqrt(probability)], [0, 0]] + update = [[0, 1 / math.sqrt(probability)], [0, 0]] self._add_unitary(update, [qubit]) def _validate_initial_statevector(self) -> None: @@ -478,7 +479,7 @@ def run( Example:: backend_options = { - "initial_statevector": np.array([1, 0, 0, 1j]) / np.sqrt(2), + "initial_statevector": np.array([1, 0, 0, 1j]) / math.sqrt(2), } """ # TODO: replace assemble with new run flow diff --git a/qiskit/quantum_info/analysis/distance.py b/qiskit/quantum_info/analysis/distance.py index 1041bce2e157..59f145eaab92 100644 --- a/qiskit/quantum_info/analysis/distance.py +++ b/qiskit/quantum_info/analysis/distance.py @@ -12,7 +12,7 @@ """A collection of discrete probability metrics.""" from __future__ import annotations -import numpy as np +import math def hellinger_distance(dist_p: dict, dist_q: dict) -> float: @@ -43,13 +43,13 @@ def hellinger_distance(dist_p: dict, dist_q: dict) -> float: total = 0 for key, val in p_normed.items(): if key in q_normed: - total += (np.sqrt(val) - np.sqrt(q_normed[key])) ** 2 + total += (math.sqrt(val) - math.sqrt(q_normed[key])) ** 2 del q_normed[key] else: total += val total += sum(q_normed.values()) - dist = np.sqrt(total) / np.sqrt(2) + dist = math.sqrt(total) / math.sqrt(2) return dist diff --git a/qiskit/quantum_info/analysis/make_observable.py b/qiskit/quantum_info/analysis/make_observable.py index b3b430f99282..63b1e82ecdf5 100644 --- a/qiskit/quantum_info/analysis/make_observable.py +++ b/qiskit/quantum_info/analysis/make_observable.py @@ -13,6 +13,7 @@ """Helper functions for building dictionaries from matrices and lists.""" from __future__ import annotations +import math import numpy as np @@ -33,7 +34,7 @@ def make_dict_observable(matrix_observable: list | np.ndarray) -> dict: dict_observable = {} observable = np.array(matrix_observable) observable_size = len(observable) - observable_bits = int(np.ceil(np.log2(observable_size))) + observable_bits = math.ceil(math.log2(observable_size)) binary_formatter = f"0{observable_bits}b" if observable.ndim == 2: observable = observable.diagonal() diff --git a/qiskit/quantum_info/analysis/z2_symmetries.py b/qiskit/quantum_info/analysis/z2_symmetries.py index 5011ba50684c..22efc9704c9b 100644 --- a/qiskit/quantum_info/analysis/z2_symmetries.py +++ b/qiskit/quantum_info/analysis/z2_symmetries.py @@ -17,6 +17,7 @@ import itertools from collections.abc import Iterable from copy import deepcopy +import math from typing import Union, cast import numpy as np @@ -121,7 +122,7 @@ def cliffords(self) -> list[SparsePauliOp]: A list of unitaries used to diagonalize the Hamiltonian. """ cliffords = [ - (SparsePauliOp(pauli_symm) + SparsePauliOp(sq_pauli)) / np.sqrt(2) + (SparsePauliOp(pauli_symm) + SparsePauliOp(sq_pauli)) / math.sqrt(2) for pauli_symm, sq_pauli in zip(self._symmetries, self._sq_paulis) ] return cliffords diff --git a/qiskit/quantum_info/operators/channel/chi.py b/qiskit/quantum_info/operators/channel/chi.py index 1b2b13fa7ea3..7cd5fce5258f 100644 --- a/qiskit/quantum_info/operators/channel/chi.py +++ b/qiskit/quantum_info/operators/channel/chi.py @@ -17,6 +17,7 @@ from __future__ import annotations import copy +import math import numpy as np from qiskit.circuit.quantumcircuit import QuantumCircuit @@ -96,7 +97,7 @@ def __init__( if output_dims: output_dim = np.prod(input_dims) if output_dims is None and input_dims is None: - output_dim = int(np.sqrt(dim_l)) + output_dim = int(math.sqrt(dim_l)) input_dim = dim_l // output_dim elif input_dims is None: input_dim = dim_l // output_dim @@ -125,7 +126,7 @@ def __init__( if output_dims is None: output_dims = data.output_dims() # Check input is N-qubit channel - num_qubits = int(np.log2(input_dim)) + num_qubits = int(math.log2(input_dim)) if 2**num_qubits != input_dim or input_dim != output_dim: raise QiskitError("Input is not an n-qubit Chi matrix.") super().__init__(chi_mat, num_qubits=num_qubits) diff --git a/qiskit/quantum_info/operators/channel/choi.py b/qiskit/quantum_info/operators/channel/choi.py index a406d9ac1e77..afd8e4fca6f9 100644 --- a/qiskit/quantum_info/operators/channel/choi.py +++ b/qiskit/quantum_info/operators/channel/choi.py @@ -17,6 +17,7 @@ from __future__ import annotations import copy +import math import numpy as np from qiskit.circuit.quantumcircuit import QuantumCircuit @@ -103,7 +104,7 @@ def __init__( if output_dims: output_dim = np.prod(output_dims) if output_dims is None and input_dims is None: - output_dim = int(np.sqrt(dim_l)) + output_dim = int(math.sqrt(dim_l)) input_dim = dim_l // output_dim elif input_dims is None: input_dim = dim_l // output_dim diff --git a/qiskit/quantum_info/operators/channel/kraus.py b/qiskit/quantum_info/operators/channel/kraus.py index c898c09f250d..625f9630b616 100644 --- a/qiskit/quantum_info/operators/channel/kraus.py +++ b/qiskit/quantum_info/operators/channel/kraus.py @@ -16,6 +16,7 @@ from __future__ import annotations import copy +import math from numbers import Number import numpy as np @@ -315,7 +316,7 @@ def _multiply(self, other): return ret # If the number is real we can update the Kraus operators # directly - val = np.sqrt(other) + val = math.sqrt(other) kraus_r = None kraus_l = [val * k for k in self._data[0]] if self._data[1] is not None: diff --git a/qiskit/quantum_info/operators/channel/ptm.py b/qiskit/quantum_info/operators/channel/ptm.py index 5b248463cdd5..1bdf1b5ef235 100644 --- a/qiskit/quantum_info/operators/channel/ptm.py +++ b/qiskit/quantum_info/operators/channel/ptm.py @@ -17,6 +17,7 @@ from __future__ import annotations import copy +import math import numpy as np from qiskit.circuit.quantumcircuit import QuantumCircuit @@ -100,11 +101,11 @@ def __init__( if input_dims: input_dim = np.prod(input_dims) else: - input_dim = int(np.sqrt(din)) + input_dim = int(math.sqrt(din)) if output_dims: output_dim = np.prod(input_dims) else: - output_dim = int(np.sqrt(dout)) + output_dim = int(math.sqrt(dout)) if output_dim**2 != dout or input_dim**2 != din or input_dim != output_dim: raise QiskitError("Invalid shape for PTM matrix.") else: @@ -127,7 +128,7 @@ def __init__( if output_dims is None: output_dims = data.output_dims() # Check input is N-qubit channel - num_qubits = int(np.log2(input_dim)) + num_qubits = int(math.log2(input_dim)) if 2**num_qubits != input_dim or input_dim != output_dim: raise QiskitError("Input is not an n-qubit Pauli transfer matrix.") super().__init__(ptm, num_qubits=num_qubits) diff --git a/qiskit/quantum_info/operators/channel/quantum_channel.py b/qiskit/quantum_info/operators/channel/quantum_channel.py index 5c482c63d0b1..16df920e2e00 100644 --- a/qiskit/quantum_info/operators/channel/quantum_channel.py +++ b/qiskit/quantum_info/operators/channel/quantum_channel.py @@ -16,6 +16,7 @@ from __future__ import annotations import copy +import math import sys from abc import abstractmethod from numbers import Number, Integral @@ -249,7 +250,7 @@ def to_instruction(self) -> Instruction: """ # Check if input is an N-qubit CPTP channel. - num_qubits = int(np.log2(self._input_dim)) + num_qubits = int(math.log2(self._input_dim)) if self._input_dim != self._output_dim or 2**num_qubits != self._input_dim: raise QiskitError( "Cannot convert QuantumChannel to Instruction: channel is not an N-qubit channel." diff --git a/qiskit/quantum_info/operators/channel/stinespring.py b/qiskit/quantum_info/operators/channel/stinespring.py index 8122d26ff325..68bbf846d38e 100644 --- a/qiskit/quantum_info/operators/channel/stinespring.py +++ b/qiskit/quantum_info/operators/channel/stinespring.py @@ -15,6 +15,7 @@ from __future__ import annotations import copy +import math from numbers import Number import numpy as np @@ -281,7 +282,7 @@ def _multiply(self, other): return ret # If the number is real we can update the Kraus operators # directly - num = np.sqrt(other) + num = math.sqrt(other) stine_l, stine_r = self._data stine_l = num * self._data[0] stine_r = None diff --git a/qiskit/quantum_info/operators/channel/superop.py b/qiskit/quantum_info/operators/channel/superop.py index 2fd4d9820b56..0d7116ef5069 100644 --- a/qiskit/quantum_info/operators/channel/superop.py +++ b/qiskit/quantum_info/operators/channel/superop.py @@ -16,6 +16,7 @@ from __future__ import annotations import copy +import math from typing import TYPE_CHECKING import numpy as np @@ -94,8 +95,8 @@ def __init__( super_mat = np.asarray(data, dtype=complex) # Determine total input and output dimensions dout, din = super_mat.shape - input_dim = int(np.sqrt(din)) - output_dim = int(np.sqrt(dout)) + input_dim = int(math.sqrt(din)) + output_dim = int(math.sqrt(dout)) if output_dim**2 != dout or input_dim**2 != din: raise QiskitError("Invalid shape for SuperOp matrix.") op_shape = OpShape.auto( diff --git a/qiskit/quantum_info/operators/channel/transformations.py b/qiskit/quantum_info/operators/channel/transformations.py index cd4ab3fa431c..18987e5e943c 100644 --- a/qiskit/quantum_info/operators/channel/transformations.py +++ b/qiskit/quantum_info/operators/channel/transformations.py @@ -18,6 +18,7 @@ """ from __future__ import annotations +import math import numpy as np from qiskit.exceptions import QiskitError @@ -328,25 +329,25 @@ def _kraus_to_superop(data): def _chi_to_choi(data, input_dim): """Transform Chi representation to a Choi representation.""" - num_qubits = int(np.log2(input_dim)) + num_qubits = int(math.log2(input_dim)) return _transform_from_pauli(data, num_qubits) def _choi_to_chi(data, input_dim): """Transform Choi representation to the Chi representation.""" - num_qubits = int(np.log2(input_dim)) + num_qubits = int(math.log2(input_dim)) return _transform_to_pauli(data, num_qubits) def _ptm_to_superop(data, input_dim): """Transform PTM representation to SuperOp representation.""" - num_qubits = int(np.log2(input_dim)) + num_qubits = int(math.log2(input_dim)) return _transform_from_pauli(data, num_qubits) def _superop_to_ptm(data, input_dim): """Transform SuperOp representation to PTM representation.""" - num_qubits = int(np.log2(input_dim)) + num_qubits = int(math.log2(input_dim)) return _transform_to_pauli(data, num_qubits) @@ -375,12 +376,12 @@ def _bipartite_tensor(mat1, mat2, shape1=None, shape2=None): dim_a0, dim_a1 = mat1.shape dim_b0, dim_b1 = mat2.shape if shape1 is None: - sdim_a0 = int(np.sqrt(dim_a0)) - sdim_a1 = int(np.sqrt(dim_a1)) + sdim_a0 = int(math.sqrt(dim_a0)) + sdim_a1 = int(math.sqrt(dim_a1)) shape1 = (sdim_a0, sdim_a0, sdim_a1, sdim_a1) if shape2 is None: - sdim_b0 = int(np.sqrt(dim_b0)) - sdim_b1 = int(np.sqrt(dim_b1)) + sdim_b0 = int(math.sqrt(dim_b0)) + sdim_b1 = int(math.sqrt(dim_b1)) shape2 = (sdim_b0, sdim_b0, sdim_b1, sdim_b1) # Check dimensions if len(shape1) != 4 or shape1[0] * shape1[1] != dim_a0 or shape1[2] * shape1[3] != dim_a1: @@ -416,7 +417,7 @@ def _transform_to_pauli(data, num_qubits): # to avoid rounding errors from square-roots of 2. cob = basis_mat for _ in range(num_qubits - 1): - dim = int(np.sqrt(len(cob))) + dim = int(math.sqrt(len(cob))) cob = np.reshape( np.transpose( np.reshape(np.kron(basis_mat, cob), (4, dim * dim, 2, 2, dim, dim)), @@ -437,7 +438,7 @@ def _transform_from_pauli(data, num_qubits): # to avoid rounding errors from square-roots of 2. cob = basis_mat for _ in range(num_qubits - 1): - dim = int(np.sqrt(len(cob))) + dim = int(math.sqrt(len(cob))) cob = np.reshape( np.transpose( np.reshape(np.kron(basis_mat, cob), (2, 2, dim, dim, 4, dim * dim)), @@ -462,6 +463,6 @@ def _check_nqubit_dim(input_dim, output_dim): raise QiskitError( f"Not an n-qubit channel: input_dim ({input_dim}) != output_dim ({output_dim})" ) - num_qubits = int(np.log2(input_dim)) + num_qubits = int(math.log2(input_dim)) if 2**num_qubits != input_dim: raise QiskitError("Not an n-qubit channel: input_dim != 2 ** n") diff --git a/qiskit/quantum_info/operators/symplectic/clifford.py b/qiskit/quantum_info/operators/symplectic/clifford.py index e1cd367d22cf..f0d30cf59f0c 100644 --- a/qiskit/quantum_info/operators/symplectic/clifford.py +++ b/qiskit/quantum_info/operators/symplectic/clifford.py @@ -16,6 +16,7 @@ import functools import itertools +import math import re from typing import Literal @@ -973,7 +974,7 @@ def bitvector(n, num_bits): @staticmethod def _unitary_matrix_to_tableau(matrix): # pylint: disable=invalid-name - num_qubits = int(np.log2(len(matrix))) + num_qubits = int(math.log2(len(matrix))) stab = np.empty((num_qubits, 2 * num_qubits + 1), dtype=bool) for i in range(num_qubits): diff --git a/qiskit/quantum_info/operators/symplectic/random.py b/qiskit/quantum_info/operators/symplectic/random.py index 999c52e6959d..1a845100b91f 100644 --- a/qiskit/quantum_info/operators/symplectic/random.py +++ b/qiskit/quantum_info/operators/symplectic/random.py @@ -14,6 +14,7 @@ """ from __future__ import annotations +import math import numpy as np from numpy.random import default_rng @@ -173,7 +174,7 @@ def _sample_qmallows(n, rng=None): m = n - i eps = 4 ** (-m) r = rng.uniform(0, 1) - index = -int(np.ceil(np.log2(r + (1 - r) * eps))) + index = -math.ceil(math.log2(r + (1 - r) * eps)) had[i] = index < m if index < m: k = index diff --git a/qiskit/quantum_info/states/statevector.py b/qiskit/quantum_info/states/statevector.py index 0f6953b3af30..b13ccde21743 100644 --- a/qiskit/quantum_info/states/statevector.py +++ b/qiskit/quantum_info/states/statevector.py @@ -15,6 +15,7 @@ """ from __future__ import annotations import copy +import math import re from numbers import Number @@ -702,7 +703,7 @@ def from_label(cls, label: str) -> Statevector: state = Statevector(data) if xy_states: # Apply hadamards to all qubits in X eigenstates - x_mat = np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) + x_mat = np.array([[1, 1], [1, -1]], dtype=complex) / math.sqrt(2) # Apply S.H to qubits in Y eigenstates y_mat = np.dot(np.diag([1, 1j]), x_mat) for qubit, char in enumerate(reversed(label)): diff --git a/qiskit/quantum_info/states/utils.py b/qiskit/quantum_info/states/utils.py index 6980daca724c..32135769033b 100644 --- a/qiskit/quantum_info/states/utils.py +++ b/qiskit/quantum_info/states/utils.py @@ -15,6 +15,7 @@ """ from __future__ import annotations +import math import numpy as np @@ -102,17 +103,18 @@ def shannon_entropy(pvec: list | np.ndarray, base: int = 2) -> float: if base == 2: def logfn(x): - return -x * np.log2(x) + return -x * math.log2(x) elif base == np.e: def logfn(x): - return -x * np.log(x) + return -x * math.log(x) else: + log_base = math.log(base) def logfn(x): - return -x * np.log(x) / np.log(base) + return -x * math.log(x) / log_base h_val = 0.0 for x in pvec: diff --git a/qiskit/result/mitigation/correlated_readout_mitigator.py b/qiskit/result/mitigation/correlated_readout_mitigator.py index e7f600f7cae2..06cc89b4c52b 100644 --- a/qiskit/result/mitigation/correlated_readout_mitigator.py +++ b/qiskit/result/mitigation/correlated_readout_mitigator.py @@ -13,6 +13,7 @@ Readout mitigator class based on the A-matrix inversion method """ +import math from typing import Optional, List, Tuple, Iterable, Callable, Union, Dict import numpy as np @@ -46,7 +47,7 @@ def __init__(self, assignment_matrix: np.ndarray, qubits: Optional[Iterable[int] if np.any(assignment_matrix < 0) or not np.allclose(np.sum(assignment_matrix, axis=0), 1): raise QiskitError("Assignment matrix columns must be valid probability distributions") assignment_matrix = np.asarray(assignment_matrix, dtype=float) - matrix_qubits_num = int(np.log2(assignment_matrix.shape[0])) + matrix_qubits_num = int(math.log2(assignment_matrix.shape[0])) if qubits is None: self._num_qubits = matrix_qubits_num self._qubits = range(self._num_qubits) @@ -260,7 +261,7 @@ def stddev_upper_bound(self, shots: int): float: the standard deviation upper bound. """ gamma = self._compute_gamma() - return gamma / np.sqrt(shots) + return gamma / math.sqrt(shots) @property def qubits(self) -> Tuple[int]: diff --git a/qiskit/result/mitigation/local_readout_mitigator.py b/qiskit/result/mitigation/local_readout_mitigator.py index 228754733997..ad71911c2d75 100644 --- a/qiskit/result/mitigation/local_readout_mitigator.py +++ b/qiskit/result/mitigation/local_readout_mitigator.py @@ -14,6 +14,7 @@ """ +import math from typing import Optional, List, Tuple, Iterable, Callable, Union, Dict import numpy as np @@ -288,7 +289,7 @@ def stddev_upper_bound(self, shots: int, qubits: List[int] = None): float: the standard deviation upper bound. """ gamma = self._compute_gamma(qubits=qubits) - return gamma / np.sqrt(shots) + return gamma / math.sqrt(shots) def _from_backend(self, backend, qubits): """Calculates amats from backend properties readout_error""" diff --git a/qiskit/result/mitigation/utils.py b/qiskit/result/mitigation/utils.py index 5500a200b56e..26b5ee373488 100644 --- a/qiskit/result/mitigation/utils.py +++ b/qiskit/result/mitigation/utils.py @@ -14,6 +14,7 @@ """ import logging +import math from typing import Optional, List, Tuple, Dict import numpy as np @@ -55,7 +56,7 @@ def expval_with_stddev(coeffs: np.ndarray, probs: np.ndarray, shots: int) -> Tup "(%f). Setting standard deviation of result to 0.", variance, ) - calc_stddev = np.sqrt(variance) if variance > 0 else 0.0 + calc_stddev = math.sqrt(variance) if variance > 0 else 0.0 return [expval, calc_stddev] @@ -63,7 +64,7 @@ def stddev(probs, shots): """Calculate stddev dict""" ret = {} for key, prob in probs.items(): - std_err = np.sqrt(prob * (1 - prob) / shots) + std_err = math.sqrt(prob * (1 - prob) / shots) ret[key] = std_err return ret diff --git a/qiskit/synthesis/discrete_basis/commutator_decompose.py b/qiskit/synthesis/discrete_basis/commutator_decompose.py index 188c9ae5b61a..67094a7105a4 100644 --- a/qiskit/synthesis/discrete_basis/commutator_decompose.py +++ b/qiskit/synthesis/discrete_basis/commutator_decompose.py @@ -92,8 +92,8 @@ def _solve_decomposition_angle(matrix: np.ndarray) -> float: lhs = math.sin(angle / 2) def objective(phi): - sin_sq = np.sin(phi / 2) ** 2 - return 2 * sin_sq * np.sqrt(1 - sin_sq**2) - lhs + sin_sq = math.sin(phi.item() / 2) ** 2 + return 2 * sin_sq * math.sqrt(1 - sin_sq**2) - lhs decomposition_angle = fsolve(objective, angle)[0] return decomposition_angle diff --git a/qiskit/synthesis/evolution/qdrift.py b/qiskit/synthesis/evolution/qdrift.py index 968d15f87bce..d4326903b4ba 100644 --- a/qiskit/synthesis/evolution/qdrift.py +++ b/qiskit/synthesis/evolution/qdrift.py @@ -12,6 +12,7 @@ """QDrift Class""" +import math from typing import Union, Optional, Callable import numpy as np from qiskit.circuit.quantumcircuit import QuantumCircuit @@ -73,7 +74,7 @@ def synthesize(self, evolution): weights = np.abs(coeffs) lambd = np.sum(weights) - num_gates = int(np.ceil(2 * (lambd**2) * (time**2) * self.reps)) + num_gates = math.ceil(2 * (lambd**2) * (time**2) * self.reps) # The protocol calls for the removal of the individual coefficients, # and multiplication by a constant evolution time. evolution_time = lambd * time / num_gates diff --git a/qiskit/synthesis/two_qubit/xx_decompose/circuits.py b/qiskit/synthesis/two_qubit/xx_decompose/circuits.py index c0f2671f42f9..42a5152f35a8 100644 --- a/qiskit/synthesis/two_qubit/xx_decompose/circuits.py +++ b/qiskit/synthesis/two_qubit/xx_decompose/circuits.py @@ -23,6 +23,7 @@ """ from __future__ import annotations +import cmath from functools import reduce import math from operator import itemgetter @@ -55,8 +56,8 @@ def decompose_xxyy_into_xxyy_xx(a_target, b_target, a_source, b_source, interact Returns the 6-tuple (r, s, u, v, x, y). """ - cplus, cminus = np.cos(a_source + b_source), np.cos(a_source - b_source) - splus, sminus = np.sin(a_source + b_source), np.sin(a_source - b_source) + cplus, cminus = math.cos(a_source + b_source), math.cos(a_source - b_source) + splus, sminus = math.sin(a_source + b_source), math.sin(a_source - b_source) ca, sa = np.cos(interaction), np.sin(interaction) uplusv = ( @@ -117,10 +118,10 @@ def decompose_xxyy_into_xxyy_xx(a_target, b_target, a_source, b_source, interact ] ) inner_phases = [ - np.angle(middle_matrix[0, 0]), - np.angle(middle_matrix[1, 1]), - np.angle(middle_matrix[1, 2]) + np.pi / 2, - np.angle(middle_matrix[0, 3]) + np.pi / 2, + cmath.phase(middle_matrix[0, 0]), + cmath.phase(middle_matrix[1, 1]), + cmath.phase(middle_matrix[1, 2]) + np.pi / 2, + cmath.phase(middle_matrix[0, 3]) + np.pi / 2, ] r, s, x, y = np.dot(phase_solver, inner_phases) @@ -133,8 +134,8 @@ def decompose_xxyy_into_xxyy_xx(a_target, b_target, a_source, b_source, interact np.kron(RZGate(2 * x).to_matrix(), RZGate(2 * y).to_matrix()), ], ) - if (abs(np.angle(generated_matrix[3, 0]) - np.pi / 2) < 0.01 and a_target > b_target) or ( - abs(np.angle(generated_matrix[3, 0]) + np.pi / 2) < 0.01 and a_target < b_target + if (abs(cmath.phase(generated_matrix[3, 0]) - np.pi / 2) < 0.01 and a_target > b_target) or ( + abs(cmath.phase(generated_matrix[3, 0]) + np.pi / 2) < 0.01 and a_target < b_target ): x += np.pi / 4 y += np.pi / 4 @@ -231,8 +232,8 @@ def xx_circuit_step(source, strength, target, embodiment): # finally conjugated by p_s_f_o. prefix_circuit.compose(permute_source_for_overlap, inplace=True) prefix_circuit.compose(source_reflection, inplace=True) - prefix_circuit.global_phase += -np.log(reflection_phase_shift).imag - prefix_circuit.global_phase += -np.log(shift_phase_shift).imag + prefix_circuit.global_phase -= cmath.phase(reflection_phase_shift) + prefix_circuit.global_phase -= cmath.phase(shift_phase_shift) # the affix circuit is constructed in reverse. # first (i.e., innermost), we install the other half of the source transformations and p_s_f_o. @@ -291,8 +292,8 @@ def canonical_xx_circuit(target, strength_sequence, basis_embodiments): circuit.compose(basis_embodiments[strength_sequence[0]], inplace=True) circuit.compose(source_reflection.inverse(), inplace=True) circuit.compose(source_shift, inplace=True) - circuit.global_phase += -np.log(shift_phase_shift).imag - circuit.global_phase += -np.log(reflection_phase_shift).imag + circuit.global_phase -= cmath.phase(shift_phase_shift) + circuit.global_phase -= cmath.phase(reflection_phase_shift) circuit.compose(affix_circuit, inplace=True) diff --git a/qiskit/synthesis/unitary/aqc/__init__.py b/qiskit/synthesis/unitary/aqc/__init__.py index abd4253d75d2..ce6577a1683a 100644 --- a/qiskit/synthesis/unitary/aqc/__init__.py +++ b/qiskit/synthesis/unitary/aqc/__init__.py @@ -108,7 +108,7 @@ unitary = ... # Define a number of qubits for the algorithm, at least 3 qubits - num_qubits = int(round(np.log2(unitary.shape[0]))) + num_qubits = round(math.log2(unitary.shape[0])) # Choose a layout of the CNOT structure for the approximate circuit, e.g. ``spin`` for # a linear layout. diff --git a/qiskit/synthesis/unitary/aqc/cnot_structures.py b/qiskit/synthesis/unitary/aqc/cnot_structures.py index 49ce1c3c7cbb..8659f0c2c343 100644 --- a/qiskit/synthesis/unitary/aqc/cnot_structures.py +++ b/qiskit/synthesis/unitary/aqc/cnot_structures.py @@ -13,6 +13,7 @@ These are the CNOT structure methods: anything that you need for creating CNOT structures. """ import logging +import math import numpy as np @@ -34,7 +35,7 @@ def _lower_limit(num_qubits: int) -> int: Returns: lower limit on the number of CNOT units. """ - num_cnots = round(np.ceil((4**num_qubits - 3 * num_qubits - 1) / 4.0)) + num_cnots = math.ceil((4**num_qubits - 3 * num_qubits - 1) / 4.0) return num_cnots diff --git a/qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py b/qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py index dc5078acf1a9..c4c9f26f7800 100644 --- a/qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py +++ b/qiskit/synthesis/unitary/aqc/fast_gradient/fast_gradient.py @@ -14,6 +14,7 @@ Implementation of the fast objective function class. """ +import math import warnings import numpy as np @@ -110,7 +111,7 @@ def gradient(self, param_values: np.ndarray) -> np.ndarray: # If thetas are the same as used for objective value calculation # before calling this function, then we re-use the computations, # otherwise we have to re-compute the objective. - tol = float(np.sqrt(np.finfo(float).eps)) + tol = math.sqrt(np.finfo(float).eps) if not np.allclose(param_values, self._circ_thetas, atol=tol, rtol=tol): self.objective(param_values) warnings.warn("gradient is computed before the objective") diff --git a/qiskit/synthesis/unitary/qsd.py b/qiskit/synthesis/unitary/qsd.py index b4c0b8a2ff2c..80a8afc1311b 100644 --- a/qiskit/synthesis/unitary/qsd.py +++ b/qiskit/synthesis/unitary/qsd.py @@ -15,6 +15,7 @@ Method is described in arXiv:quant-ph/0406176. """ from __future__ import annotations +import math from typing import Callable import scipy import numpy as np @@ -93,7 +94,7 @@ def qs_decomposition( """ # _depth (int): Internal use parameter to track recursion depth. dim = mat.shape[0] - nqubits = int(np.log2(dim)) + nqubits = int(math.log2(dim)) if np.allclose(np.identity(dim), mat): return QuantumCircuit(nqubits) if dim == 2: @@ -185,7 +186,7 @@ def _demultiplex(um0, um1, opt_a1=False, opt_a2=False, *, _depth=0): QuantumCircuit: decomposed circuit """ dim = um0.shape[0] + um1.shape[0] # these should be same dimension - nqubits = int(np.log2(dim)) + nqubits = int(math.log2(dim)) um0um1 = um0 @ um1.T.conjugate() if is_hermitian_matrix(um0um1): eigvals, vmat = scipy.linalg.eigh(um0um1) diff --git a/qiskit/transpiler/passes/calibration/rzx_builder.py b/qiskit/transpiler/passes/calibration/rzx_builder.py index 36bbe5b365b7..4cb576a23bc9 100644 --- a/qiskit/transpiler/passes/calibration/rzx_builder.py +++ b/qiskit/transpiler/passes/calibration/rzx_builder.py @@ -14,6 +14,7 @@ from __future__ import annotations import enum +import math import warnings from collections.abc import Sequence from math import pi, erf @@ -135,7 +136,7 @@ def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> i # The error function is used because the Gaussian may have chopped tails. # Area is normalized by amplitude. # This makes widths robust to the rounding error. - risefall_area = params["sigma"] * np.sqrt(2 * pi) * erf(risefall_sigma_ratio) + risefall_area = params["sigma"] * math.sqrt(2 * pi) * erf(risefall_sigma_ratio) full_area = params["width"] + risefall_area # Get estimate of target area. Assume this is pi/2 controlled rotation. diff --git a/qiskit/transpiler/passes/synthesis/aqc_plugin.py b/qiskit/transpiler/passes/synthesis/aqc_plugin.py index 4c9b4b9dae97..35987671f9c9 100644 --- a/qiskit/transpiler/passes/synthesis/aqc_plugin.py +++ b/qiskit/transpiler/passes/synthesis/aqc_plugin.py @@ -20,7 +20,7 @@ AQCSynthesisPlugin """ from functools import partial -import numpy as np +import math from qiskit.converters import circuit_to_dag from qiskit.transpiler.passes.synthesis.plugin import UnitarySynthesisPlugin @@ -118,7 +118,7 @@ def run(self, unitary, **options): from qiskit.synthesis.unitary.aqc.cnot_unit_circuit import CNOTUnitCircuit from qiskit.synthesis.unitary.aqc.fast_gradient.fast_gradient import FastCNOTUnitObjective - num_qubits = int(round(np.log2(unitary.shape[0]))) + num_qubits = round(math.log2(unitary.shape[0])) config = options.get("config") or {} diff --git a/qiskit/visualization/bloch.py b/qiskit/visualization/bloch.py index 778e4a013546..bae0633a811c 100644 --- a/qiskit/visualization/bloch.py +++ b/qiskit/visualization/bloch.py @@ -48,6 +48,7 @@ __all__ = ["Bloch"] +import math import os import numpy as np import matplotlib @@ -648,9 +649,7 @@ def plot_points(self): ) elif self.point_style[k] == "m": - pnt_colors = np.array( - self.point_color * int(np.ceil(num / float(len(self.point_color)))) - ) + pnt_colors = np.array(self.point_color * math.ceil(num / len(self.point_color))) pnt_colors = pnt_colors[0:num] pnt_colors = list(pnt_colors[indperm]) diff --git a/qiskit/visualization/state_visualization.py b/qiskit/visualization/state_visualization.py index 80fcdb2bc22d..e862b0208e28 100644 --- a/qiskit/visualization/state_visualization.py +++ b/qiskit/visualization/state_visualization.py @@ -17,6 +17,7 @@ Visualization functions for quantum states. """ +import math from typing import List, Union from functools import reduce import colorsys @@ -97,7 +98,7 @@ def plot_state_hinton(state, title="", figsize=None, ax_real=None, ax_imag=None, num = rho.num_qubits if num is None: raise VisualizationError("Input is not a multi-qubit quantum state.") - max_weight = 2 ** np.ceil(np.log(np.abs(rho.data).max()) / np.log(2)) + max_weight = 2 ** math.ceil(math.log2(np.abs(rho.data).max())) datareal = np.real(rho.data) dataimag = np.imag(rho.data) @@ -1314,7 +1315,7 @@ def _state_to_latex_ket( Returns: String with LaTeX representation of the state vector """ - num = int(np.log2(len(data))) + num = int(math.log2(len(data))) def ket_name(i): return bin(i)[2:].zfill(num)