From 2cd42b493274984f4f03701e7e745a5516c5ae78 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Fri, 13 Aug 2021 15:22:11 +0100 Subject: [PATCH] Add type hints to gates and QuantumCircuit (#6831) * Move QuantumCircuit.measure into QuantumCircuit * Add type hints to QuantumCircuit This adds full type hints to all public function entry points in QuantumCircuit. This will not cause quantumcircuit.py to pass a mypy check yet; there are various idioms used within the file which are not statically type-safe (nor, in some cases, dynamically type safe in the sense that we sometimes rely on mismatched types to throw exceptions). These include anything which takes the `inplace` argument, since `inplace=True` versions do not return, which influences the out types to Optional["QuantumCircuit"] This then means that methods using these functions (e.g. `QuantumCircuit.__add__`) become unsafe; even though they use the default `inplace=False`, subclasses could override this while still maintaining type safety, and so the operation is not safe. This patch makes no attempt to modify the behaviour of these non-type-safe functions, only to classify what the current types are. * Add type hints and label to all standard gates This completes the standard-library gate documentation overhaul tracked by #3705, and mostly done in #4017, by adding type hints to the initializers. It also adds the missing `label` option to the iSWAP, R, RXX, RYY, RZX and RZZ gates, to bring them up to parity with the other gates, tracked in #6780. * Add parameters to QuantumCircuit gate docstrings This only adds short documentation to the docstrings of the gate methods (for example `QuantumCircuit.x`) about the parameters and the return values. It does not currently import the matrix representations into the docstrings themselves, because the gate classes include those. The expectation is that you would see the docstring when running interactively, and even in Jupyter, this typically does not include displaying maths. The matrix form is more useful when looking at the online Sphinx documentation, where the cross-reference gate link will work more easily. * Add release note * Document type aliases in comments, not __doc__ Python 3.6 doesn't allow assigning to the __doc__ field of the `typing` types, so as long as we support it, we can't do that. * Include reset in QuantumCircuit The previous commit stopped `QuantumCircuit.measure` from being monkey-patched in by `qiskit/circuit/measure.py`, but the similar `QuantumCircuit.reset` operation was overlooked. This adds it in for consistency. --- qiskit/circuit/gate.py | 2 +- qiskit/circuit/library/standard_gates/h.py | 12 +- qiskit/circuit/library/standard_gates/i.py | 3 +- .../circuit/library/standard_gates/iswap.py | 5 +- qiskit/circuit/library/standard_gates/ms.py | 4 +- .../multi_control_rotation_gates.py | 30 +- qiskit/circuit/library/standard_gates/p.py | 34 +- qiskit/circuit/library/standard_gates/r.py | 8 +- qiskit/circuit/library/standard_gates/rx.py | 19 +- qiskit/circuit/library/standard_gates/rxx.py | 6 +- qiskit/circuit/library/standard_gates/ry.py | 18 +- qiskit/circuit/library/standard_gates/ryy.py | 6 +- qiskit/circuit/library/standard_gates/rz.py | 18 +- qiskit/circuit/library/standard_gates/rzx.py | 6 +- qiskit/circuit/library/standard_gates/rzz.py | 6 +- qiskit/circuit/library/standard_gates/s.py | 5 +- qiskit/circuit/library/standard_gates/swap.py | 12 +- qiskit/circuit/library/standard_gates/sx.py | 14 +- qiskit/circuit/library/standard_gates/t.py | 5 +- qiskit/circuit/library/standard_gates/u.py | 27 +- qiskit/circuit/library/standard_gates/u1.py | 40 +- qiskit/circuit/library/standard_gates/u2.py | 6 +- qiskit/circuit/library/standard_gates/u3.py | 26 +- qiskit/circuit/library/standard_gates/x.py | 136 +- qiskit/circuit/library/standard_gates/y.py | 12 +- qiskit/circuit/library/standard_gates/z.py | 12 +- qiskit/circuit/measure.py | 21 - qiskit/circuit/parameterexpression.py | 8 + qiskit/circuit/quantumcircuit.py | 1543 ++++++++++++++--- qiskit/circuit/reset.py | 9 - .../gate-type-hints-b287215190144ceb.yaml | 5 + 31 files changed, 1662 insertions(+), 396 deletions(-) create mode 100644 releasenotes/notes/gate-type-hints-b287215190144ceb.yaml diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 39f05861972e..566f054ab22e 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -93,7 +93,7 @@ def _return_repeat(self, exponent: float) -> "Gate": def control( self, - num_ctrl_qubits: Optional[int] = 1, + num_ctrl_qubits: int = 1, label: Optional[str] = None, ctrl_state: Optional[Union[int, str]] = None, ): diff --git a/qiskit/circuit/library/standard_gates/h.py b/qiskit/circuit/library/standard_gates/h.py index a4071aeed61a..cf3da2ec768f 100644 --- a/qiskit/circuit/library/standard_gates/h.py +++ b/qiskit/circuit/library/standard_gates/h.py @@ -12,6 +12,7 @@ """Hadamard gate.""" +from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate @@ -47,7 +48,7 @@ class HGate(Gate): \end{pmatrix} """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new H gate.""" super().__init__("h", 1, [], label=label) @@ -67,7 +68,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[int, str]] = None, + ): """Return a (multi-)controlled-H gate. One control qubit returns a CH gate. @@ -158,7 +164,7 @@ class CHGate(ControlledGate): dtype=complex, ) - def __init__(self, label=None, ctrl_state=None): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[int, str]] = None): """Create new CH gate.""" super().__init__( "ch", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=HGate() diff --git a/qiskit/circuit/library/standard_gates/i.py b/qiskit/circuit/library/standard_gates/i.py index 67ece09f9322..67015fa05574 100644 --- a/qiskit/circuit/library/standard_gates/i.py +++ b/qiskit/circuit/library/standard_gates/i.py @@ -12,6 +12,7 @@ """Identity gate.""" +from typing import Optional import numpy from qiskit.circuit.gate import Gate @@ -39,7 +40,7 @@ class IGate(Gate): └───┘ """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new Identity gate.""" super().__init__("id", 1, [], label=label) diff --git a/qiskit/circuit/library/standard_gates/iswap.py b/qiskit/circuit/library/standard_gates/iswap.py index 6e21951b3e80..66734edc81fa 100644 --- a/qiskit/circuit/library/standard_gates/iswap.py +++ b/qiskit/circuit/library/standard_gates/iswap.py @@ -12,6 +12,7 @@ """iSWAP gate.""" +from typing import Optional import numpy as np from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister @@ -75,9 +76,9 @@ class iSwapGate(Gate): \end{pmatrix} """ - def __init__(self): + def __init__(self, label: Optional[str] = None): """Create new iSwap gate.""" - super().__init__("iswap", 2, []) + super().__init__("iswap", 2, [], label=label) def _define(self): """ diff --git a/qiskit/circuit/library/standard_gates/ms.py b/qiskit/circuit/library/standard_gates/ms.py index 6681f02ea5ee..818c752f7376 100644 --- a/qiskit/circuit/library/standard_gates/ms.py +++ b/qiskit/circuit/library/standard_gates/ms.py @@ -12,9 +12,11 @@ """Global Mølmer–Sørensen gate.""" +from typing import Optional import warnings from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class MSGate(Gate): @@ -30,7 +32,7 @@ class MSGate(Gate): and is thus reduced to the RXXGate. """ - def __init__(self, num_qubits, theta, label=None): + def __init__(self, num_qubits: int, theta: ParameterValueType, label: Optional[str] = None): """Create new MS gate.""" warnings.warn( "The qiskit.circuit.library.standard_gates.ms import " 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 20381c285bca..bbab67935b3d 100644 --- a/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +++ b/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py @@ -14,9 +14,11 @@ """ from math import pi +from typing import Optional, Union, Tuple, List from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit from qiskit.circuit.library.standard_gates.x import MCXGate from qiskit.circuit.library.standard_gates.u3 import _generate_gray_code +from qiskit.circuit.parameterexpression import ParameterValueType from qiskit.exceptions import QiskitError @@ -74,14 +76,20 @@ def _apply_mcu_graycode(circuit, theta, phi, lam, ctls, tgt, use_basis_gates): last_pattern = pattern -def mcrx(self, theta, q_controls, q_target, use_basis_gates=False): +def mcrx( + self, + theta: ParameterValueType, + q_controls: Union[QuantumRegister, List[Qubit]], + q_target: Qubit, + use_basis_gates: bool = False, +): """ Apply Multiple-Controlled X rotation gate Args: self (QuantumCircuit): The QuantumCircuit object to apply the mcrx gate on. theta (float): angle theta - q_controls (list(Qubit)): The list of control qubits + q_controls (QuantumRegister or list(Qubit)): The list of control qubits q_target (Qubit): The target qubit use_basis_gates (bool): use p, u, cx @@ -134,7 +142,15 @@ def mcrx(self, theta, q_controls, q_target, use_basis_gates=False): ) -def mcry(self, theta, q_controls, q_target, q_ancillae=None, mode=None, use_basis_gates=False): +def mcry( + self, + theta: ParameterValueType, + q_controls: Union[QuantumRegister, List[Qubit]], + q_target: Qubit, + q_ancillae: Optional[Union[QuantumRegister, Tuple[QuantumRegister, int]]] = None, + mode: str = None, + use_basis_gates=False, +): """ Apply Multiple-Controlled Y rotation gate @@ -219,7 +235,13 @@ def mcry(self, theta, q_controls, q_target, q_ancillae=None, mode=None, use_basi raise QiskitError(f"Unrecognized mode for building MCRY circuit: {mode}.") -def mcrz(self, lam, q_controls, q_target, use_basis_gates=False): +def mcrz( + self, + lam: ParameterValueType, + q_controls: Union[QuantumRegister, List[Qubit]], + q_target: Qubit, + use_basis_gates: bool = False, +): """ Apply Multiple-Controlled Z rotation gate diff --git a/qiskit/circuit/library/standard_gates/p.py b/qiskit/circuit/library/standard_gates/p.py index 03388cba5555..9fc53e6390b3 100644 --- a/qiskit/circuit/library/standard_gates/p.py +++ b/qiskit/circuit/library/standard_gates/p.py @@ -12,10 +12,12 @@ """Phase Gate.""" +from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class PhaseGate(Gate): @@ -69,7 +71,7 @@ class PhaseGate(Gate): `1612.00858 `_ """ - def __init__(self, theta, label=None): + def __init__(self, theta: ParameterValueType, label: Optional[str] = None): """Create new Phase gate.""" super().__init__("p", 1, [theta], label=label) @@ -83,7 +85,12 @@ def _define(self): qc.append(UGate(0, 0, self.params[0]), [0]) self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[int, str]] = None, + ): """Return a (multi-)controlled-Phase gate. Args: @@ -153,7 +160,12 @@ class CPhaseGate(ControlledGate): phase difference. """ - def __init__(self, theta, label=None, ctrl_state=None): + def __init__( + self, + theta: ParameterValueType, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create new CPhase gate.""" super().__init__( "cp", @@ -185,7 +197,12 @@ def _define(self): qc.p(self.params[0] / 2, 1) self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Controlled version of this gate. Args: @@ -242,7 +259,7 @@ class MCPhaseGate(ControlledGate): The singly-controlled-version of this gate. """ - def __init__(self, lam, num_ctrl_qubits, label=None): + def __init__(self, lam: ParameterValueType, num_ctrl_qubits: int, label: Optional[str] = None): """Create new MCPhase gate.""" super().__init__( "mcphase", @@ -273,7 +290,12 @@ def _define(self): qc.data = definition self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Controlled version of this gate. Args: diff --git a/qiskit/circuit/library/standard_gates/r.py b/qiskit/circuit/library/standard_gates/r.py index 86adff2dfd99..9907253ca430 100644 --- a/qiskit/circuit/library/standard_gates/r.py +++ b/qiskit/circuit/library/standard_gates/r.py @@ -13,10 +13,12 @@ """Rotation around an axis in x-y plane.""" import math +from typing import Optional import numpy from qiskit.qasm import pi from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class RGate(Gate): @@ -43,9 +45,11 @@ class RGate(Gate): \end{pmatrix} """ - def __init__(self, theta, phi): + def __init__( + self, theta: ParameterValueType, phi: ParameterValueType, label: Optional[str] = None + ): """Create new r single-qubit gate.""" - super().__init__("r", 1, [theta, phi]) + super().__init__("r", 1, [theta, phi], label=label) def _define(self): """ diff --git a/qiskit/circuit/library/standard_gates/rx.py b/qiskit/circuit/library/standard_gates/rx.py index 163484646527..062642097737 100644 --- a/qiskit/circuit/library/standard_gates/rx.py +++ b/qiskit/circuit/library/standard_gates/rx.py @@ -13,11 +13,14 @@ """Rotation around the X axis.""" import math +from typing import Optional, Union import numpy + from qiskit.qasm import pi from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class RXGate(Gate): @@ -44,7 +47,7 @@ class RXGate(Gate): \end{pmatrix} """ - def __init__(self, theta, label=None): + def __init__(self, theta: ParameterValueType, label: Optional[str] = None): """Create new RX gate.""" super().__init__("rx", 1, [theta], label=label) @@ -64,7 +67,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-RX gate. Args: @@ -151,7 +159,12 @@ class CRXGate(ControlledGate): \end{pmatrix} """ - def __init__(self, theta, label=None, ctrl_state=None): + def __init__( + self, + theta: ParameterValueType, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create new CRX gate.""" super().__init__( "crx", diff --git a/qiskit/circuit/library/standard_gates/rxx.py b/qiskit/circuit/library/standard_gates/rxx.py index 7208a06cfe78..d46e074327a0 100644 --- a/qiskit/circuit/library/standard_gates/rxx.py +++ b/qiskit/circuit/library/standard_gates/rxx.py @@ -12,8 +12,10 @@ """Two-qubit XX-rotation gate.""" +from typing import Optional from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class RXXGate(Gate): @@ -66,9 +68,9 @@ class RXXGate(Gate): \end{pmatrix} """ - def __init__(self, theta): + def __init__(self, theta: ParameterValueType, label: Optional[str] = None): """Create new RXX gate.""" - super().__init__("rxx", 2, [theta]) + super().__init__("rxx", 2, [theta], label=label) def _define(self): """Calculate a subcircuit that implements this unitary.""" diff --git a/qiskit/circuit/library/standard_gates/ry.py b/qiskit/circuit/library/standard_gates/ry.py index 09055f347eb1..eea2d4272332 100644 --- a/qiskit/circuit/library/standard_gates/ry.py +++ b/qiskit/circuit/library/standard_gates/ry.py @@ -13,11 +13,13 @@ """Rotation around the Y axis.""" import math +from typing import Optional, Union import numpy from qiskit.qasm import pi from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class RYGate(Gate): @@ -44,7 +46,7 @@ class RYGate(Gate): \end{pmatrix} """ - def __init__(self, theta, label=None): + def __init__(self, theta: ParameterValueType, label: Optional[str] = None): """Create new RY gate.""" super().__init__("ry", 1, [theta], label=label) @@ -64,7 +66,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-RY gate. Args: @@ -151,7 +158,12 @@ class CRYGate(ControlledGate): \end{pmatrix} """ - def __init__(self, theta, label=None, ctrl_state=None): + def __init__( + self, + theta: ParameterValueType, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create new CRY gate.""" super().__init__( "cry", diff --git a/qiskit/circuit/library/standard_gates/ryy.py b/qiskit/circuit/library/standard_gates/ryy.py index cdb284cee95d..54001cb0f84e 100644 --- a/qiskit/circuit/library/standard_gates/ryy.py +++ b/qiskit/circuit/library/standard_gates/ryy.py @@ -12,9 +12,11 @@ """Two-qubit YY-rotation gate.""" +from typing import Optional import numpy as np from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class RYYGate(Gate): @@ -67,9 +69,9 @@ class RYYGate(Gate): \end{pmatrix} """ - def __init__(self, theta): + def __init__(self, theta: ParameterValueType, label: Optional[str] = None): """Create new RYY gate.""" - super().__init__("ryy", 2, [theta]) + super().__init__("ryy", 2, [theta], label=label) def _define(self): """Calculate a subcircuit that implements this unitary.""" diff --git a/qiskit/circuit/library/standard_gates/rz.py b/qiskit/circuit/library/standard_gates/rz.py index 141b27f62172..9591b8532083 100644 --- a/qiskit/circuit/library/standard_gates/rz.py +++ b/qiskit/circuit/library/standard_gates/rz.py @@ -12,9 +12,11 @@ """Rotation around the Z axis.""" +from typing import Optional, Union from qiskit.circuit.gate import Gate from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class RZGate(Gate): @@ -54,7 +56,7 @@ class RZGate(Gate): `1612.00858 `_ """ - def __init__(self, phi, label=None): + def __init__(self, phi: ParameterValueType, label: Optional[str] = None): """Create new RZ gate.""" super().__init__("rz", 1, [phi], label=label) @@ -75,7 +77,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-RZ gate. Args: @@ -169,7 +176,12 @@ class CRZGate(ControlledGate): phase difference. """ - def __init__(self, theta, label=None, ctrl_state=None): + def __init__( + self, + theta: ParameterValueType, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create new CRZ gate.""" super().__init__( "crz", diff --git a/qiskit/circuit/library/standard_gates/rzx.py b/qiskit/circuit/library/standard_gates/rzx.py index bb03ab04cf28..203a827f34e0 100644 --- a/qiskit/circuit/library/standard_gates/rzx.py +++ b/qiskit/circuit/library/standard_gates/rzx.py @@ -12,8 +12,10 @@ """Two-qubit ZX-rotation gate.""" +from typing import Optional from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class RZXGate(Gate): @@ -112,9 +114,9 @@ class RZXGate(Gate): \end{pmatrix} """ - def __init__(self, theta): + def __init__(self, theta: ParameterValueType, label: Optional[str] = None): """Create new RZX gate.""" - super().__init__("rzx", 2, [theta]) + super().__init__("rzx", 2, [theta], label=label) def _define(self): """ diff --git a/qiskit/circuit/library/standard_gates/rzz.py b/qiskit/circuit/library/standard_gates/rzz.py index 062c8f5e7d2f..80c9e4693141 100644 --- a/qiskit/circuit/library/standard_gates/rzz.py +++ b/qiskit/circuit/library/standard_gates/rzz.py @@ -12,8 +12,10 @@ """Two-qubit ZZ-rotation gate.""" +from typing import Optional from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterValueType class RZZGate(Gate): @@ -79,9 +81,9 @@ class RZZGate(Gate): \end{pmatrix} """ - def __init__(self, theta): + def __init__(self, theta: ParameterValueType, label: Optional[str] = None): """Create new RZZ gate.""" - super().__init__("rzz", 2, [theta]) + super().__init__("rzz", 2, [theta], label=label) def _define(self): """ diff --git a/qiskit/circuit/library/standard_gates/s.py b/qiskit/circuit/library/standard_gates/s.py index d7890f27513f..a5e8d5ce7348 100644 --- a/qiskit/circuit/library/standard_gates/s.py +++ b/qiskit/circuit/library/standard_gates/s.py @@ -12,6 +12,7 @@ """The S and Sdg gate.""" +from typing import Optional import numpy from qiskit.qasm import pi from qiskit.circuit.gate import Gate @@ -45,7 +46,7 @@ class SGate(Gate): Equivalent to a :math:`\pi/2` radian rotation about the Z axis. """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new S gate.""" super().__init__("s", 1, [], label=label) @@ -101,7 +102,7 @@ class SdgGate(Gate): Equivalent to a :math:`\pi/2` radian rotation about the Z axis. """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new Sdg gate.""" super().__init__("sdg", 1, [], label=label) diff --git a/qiskit/circuit/library/standard_gates/swap.py b/qiskit/circuit/library/standard_gates/swap.py index ce300040f438..fec6c388c24d 100644 --- a/qiskit/circuit/library/standard_gates/swap.py +++ b/qiskit/circuit/library/standard_gates/swap.py @@ -12,6 +12,7 @@ """Swap gate.""" +from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate @@ -50,7 +51,7 @@ class SwapGate(Gate): |a, b\rangle \rightarrow |b, a\rangle """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new SWAP gate.""" super().__init__("swap", 2, [], label=label) @@ -74,7 +75,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-SWAP gate. One control returns a CSWAP (Fredkin) gate. @@ -201,7 +207,7 @@ class CSwapGate(ControlledGate): ] ) - def __init__(self, label=None, ctrl_state=None): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CSWAP gate.""" super().__init__( "cswap", diff --git a/qiskit/circuit/library/standard_gates/sx.py b/qiskit/circuit/library/standard_gates/sx.py index f5242cb732a8..0c14c6c3e359 100644 --- a/qiskit/circuit/library/standard_gates/sx.py +++ b/qiskit/circuit/library/standard_gates/sx.py @@ -12,6 +12,7 @@ """Sqrt(X) and C-Sqrt(X) gates.""" +from typing import Optional, Union import numpy from qiskit.qasm import pi from qiskit.circuit.controlledgate import ControlledGate @@ -54,7 +55,7 @@ class SXGate(Gate): """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new SX gate.""" super().__init__("sx", 1, [], label=label) @@ -77,7 +78,12 @@ def inverse(self): """Return inverse SX gate (i.e. SXdg).""" return SXdgGate() - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-SX gate. One control returns a CSX gate. @@ -128,7 +134,7 @@ class SXdgGate(Gate): """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new SXdg gate.""" super().__init__("sxdg", 1, [], label=label) @@ -226,7 +232,7 @@ class CSXGate(ControlledGate): ] ) - def __init__(self, label=None, ctrl_state=None): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CSX gate.""" super().__init__( "csx", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=SXGate() diff --git a/qiskit/circuit/library/standard_gates/t.py b/qiskit/circuit/library/standard_gates/t.py index feee34cea7a5..2e2a5b296fa9 100644 --- a/qiskit/circuit/library/standard_gates/t.py +++ b/qiskit/circuit/library/standard_gates/t.py @@ -12,6 +12,7 @@ """T and Tdg gate.""" +from typing import Optional import numpy from qiskit.qasm import pi from qiskit.circuit.gate import Gate @@ -46,7 +47,7 @@ class TGate(Gate): Equivalent to a :math:`\pi/4` radian rotation about the Z axis. """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new T gate.""" super().__init__("t", 1, [], label=label) @@ -102,7 +103,7 @@ class TdgGate(Gate): Equivalent to a :math:`\pi/2` radian rotation about the Z axis. """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new Tdg gate.""" super().__init__("tdg", 1, [], label=label) diff --git a/qiskit/circuit/library/standard_gates/u.py b/qiskit/circuit/library/standard_gates/u.py index 9586443fdcae..880cd8179922 100644 --- a/qiskit/circuit/library/standard_gates/u.py +++ b/qiskit/circuit/library/standard_gates/u.py @@ -12,9 +12,11 @@ """Two-pulse single-qubit gate.""" +from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate +from qiskit.circuit.parameterexpression import ParameterValueType from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.exceptions import CircuitError @@ -59,7 +61,13 @@ class UGate(Gate): U(\theta, 0, 0) = RY(\theta) """ - def __init__(self, theta, phi, lam, label=None): + def __init__( + self, + theta: ParameterValueType, + phi: ParameterValueType, + lam: ParameterValueType, + label: Optional[str] = None, + ): """Create new U gate.""" super().__init__("u", 1, [theta, phi, lam], label=label) @@ -70,7 +78,12 @@ def inverse(self): """ return UGate(-self.params[0], -self.params[2], -self.params[1]) - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-U gate. Args: @@ -168,7 +181,15 @@ class CUGate(ControlledGate): \end{pmatrix} """ - def __init__(self, theta, phi, lam, gamma, label=None, ctrl_state=None): + def __init__( + self, + theta: ParameterValueType, + phi: ParameterValueType, + lam: ParameterValueType, + gamma: ParameterValueType, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create new CU gate.""" super().__init__( "cu", diff --git a/qiskit/circuit/library/standard_gates/u1.py b/qiskit/circuit/library/standard_gates/u1.py index 7a303882dc6e..5828987747cf 100644 --- a/qiskit/circuit/library/standard_gates/u1.py +++ b/qiskit/circuit/library/standard_gates/u1.py @@ -12,9 +12,11 @@ """U1 Gate.""" +from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate +from qiskit.circuit.parameterexpression import ParameterValueType from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import _ctrl_state_to_int @@ -74,7 +76,7 @@ class U1Gate(Gate): `1612.00858 `_ """ - def __init__(self, theta, label=None): + def __init__(self, theta: ParameterValueType, label: Optional[str] = None): """Create new U1 gate.""" super().__init__("u1", 1, [theta], label=label) @@ -91,7 +93,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-U1 gate. Args: @@ -161,7 +168,12 @@ class CU1Gate(ControlledGate): phase difference. """ - def __init__(self, theta, label=None, ctrl_state=None): + def __init__( + self, + theta: ParameterValueType, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create new CU1 gate.""" super().__init__( "cu1", @@ -199,7 +211,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Controlled version of this gate. Args: @@ -259,7 +276,13 @@ class MCU1Gate(ControlledGate): The singly-controlled-version of this gate. """ - def __init__(self, lam, num_ctrl_qubits, label=None, ctrl_state=None): + def __init__( + self, + lam: ParameterValueType, + num_ctrl_qubits: int, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create new MCU1 gate.""" super().__init__( "mcu1", @@ -292,7 +315,12 @@ def _define(self): qc._append(instr, qargs, cargs) self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Controlled version of this gate. Args: diff --git a/qiskit/circuit/library/standard_gates/u2.py b/qiskit/circuit/library/standard_gates/u2.py index 4be1e3b86621..fb08d9fe1264 100644 --- a/qiskit/circuit/library/standard_gates/u2.py +++ b/qiskit/circuit/library/standard_gates/u2.py @@ -12,9 +12,11 @@ """One-pulse single-qubit gate.""" +from typing import Optional import numpy from qiskit.qasm import pi from qiskit.circuit.gate import Gate +from qiskit.circuit.parameterexpression import ParameterValueType from qiskit.circuit.quantumregister import QuantumRegister @@ -58,7 +60,9 @@ class U2Gate(Gate): using two X90 pulses. """ - def __init__(self, phi, lam, label=None): + def __init__( + self, phi: ParameterValueType, lam: ParameterValueType, label: Optional[str] = None + ): """Create new U2 gate.""" super().__init__("u2", 1, [phi, lam], label=label) diff --git a/qiskit/circuit/library/standard_gates/u3.py b/qiskit/circuit/library/standard_gates/u3.py index 9145978fc308..58947a6b439d 100644 --- a/qiskit/circuit/library/standard_gates/u3.py +++ b/qiskit/circuit/library/standard_gates/u3.py @@ -12,9 +12,11 @@ """Two-pulse single-qubit gate.""" +from typing import Optional, Union import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate +from qiskit.circuit.parameterexpression import ParameterValueType from qiskit.circuit.quantumregister import QuantumRegister @@ -58,7 +60,13 @@ class U3Gate(Gate): U3(\theta, 0, 0) = RY(\theta) """ - def __init__(self, theta, phi, lam, label=None): + def __init__( + self, + theta: ParameterValueType, + phi: ParameterValueType, + lam: ParameterValueType, + label: Optional[str] = None, + ): """Create new U3 gate.""" super().__init__("u3", 1, [theta, phi, lam], label=label) @@ -69,7 +77,12 @@ def inverse(self): """ return U3Gate(-self.params[0], -self.params[2], -self.params[1]) - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-U3 gate. Args: @@ -169,7 +182,14 @@ class CU3Gate(ControlledGate): \end{pmatrix} """ - def __init__(self, theta, phi, lam, label=None, ctrl_state=None): + def __init__( + self, + theta: ParameterValueType, + phi: ParameterValueType, + lam: ParameterValueType, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create new CU3 gate.""" super().__init__( "cu3", diff --git a/qiskit/circuit/library/standard_gates/x.py b/qiskit/circuit/library/standard_gates/x.py index f312d29e87ad..a0a9e9e778d0 100644 --- a/qiskit/circuit/library/standard_gates/x.py +++ b/qiskit/circuit/library/standard_gates/x.py @@ -12,11 +12,13 @@ """X, CX, CCX and multi-controlled X gates.""" +from typing import Optional, Union import warnings from math import ceil import numpy from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate +from qiskit.circuit.parameterexpression import ParameterValueType from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit._utils import _compute_control_matrix, _ctrl_state_to_int from qiskit.qasm import pi @@ -70,7 +72,7 @@ class XGate(Gate): |1\rangle \rightarrow |0\rangle """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new X gate.""" super().__init__("x", 1, [], label=label) @@ -90,7 +92,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-X gate. One control returns a CX gate. Two controls returns a CCX gate. @@ -176,13 +183,18 @@ class CXGate(ControlledGate): `|a, b\rangle \rightarrow |a, a \oplus b\rangle` """ - def __init__(self, label=None, ctrl_state=None): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CX gate.""" super().__init__( "cx", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=XGate() ) - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a controlled-X gate with more control lines. Args: @@ -280,7 +292,7 @@ class CCXGate(ControlledGate): """ - def __init__(self, label=None, ctrl_state=None): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CCX gate.""" super().__init__( "ccx", 3, [], num_ctrl_qubits=2, label=label, ctrl_state=ctrl_state, base_gate=XGate() @@ -322,7 +334,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Controlled version of this gate. Args: @@ -367,7 +384,7 @@ class RCCXGate(Gate): of Fig. 3. """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create a new simplified CCX gate.""" super().__init__("rccx", 3, [], label=label) @@ -432,7 +449,13 @@ class C3SXGate(ControlledGate): [1] Barenco et al., 1995. https://arxiv.org/pdf/quant-ph/9503016.pdf """ - def __init__(self, label=None, ctrl_state=None, *, angle=None): + def __init__( + self, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + *, + angle: Optional[ParameterValueType] = None, + ): """Create a new 3-qubit controlled sqrt-X gate. Args: @@ -536,7 +559,12 @@ class C3XGate(ControlledGate): This implementation uses :math:`\sqrt{T}` and 14 CNOT gates. """ - def __new__(cls, angle=None, label=None, ctrl_state=None): + def __new__( + cls, + angle: Optional[ParameterValueType] = None, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): if angle is not None: return C3SXGate(label, ctrl_state, angle=angle) @@ -545,7 +573,12 @@ def __new__(cls, angle=None, label=None, ctrl_state=None): return instance # pylint: disable=unused-argument - def __init__(self, angle=None, label=None, ctrl_state=None): + def __init__( + self, + angle: Optional[ParameterValueType] = None, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create a new 3-qubit controlled X gate.""" super().__init__( "mcx", 4, [], num_ctrl_qubits=3, label=label, ctrl_state=ctrl_state, base_gate=XGate() @@ -624,7 +657,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Controlled version of this gate. Args: @@ -667,7 +705,7 @@ class RC3XGate(Gate): of Fig. 4. """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create a new RC3X gate.""" super().__init__("rcccx", 4, [], label=label) @@ -760,7 +798,7 @@ class C4XGate(ControlledGate): [2] Maslov, 2015. https://arxiv.org/abs/1508.03273 """ - def __init__(self, label=None, ctrl_state=None): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create a new 4-qubit controlled X gate.""" super().__init__( "mcx", 5, [], num_ctrl_qubits=4, label=label, ctrl_state=ctrl_state, base_gate=XGate() @@ -816,7 +854,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Controlled version of this gate. Args: @@ -851,7 +894,12 @@ def __array__(self, dtype=None): class MCXGate(ControlledGate): """The general, multi-controlled X gate.""" - def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None): + def __new__( + cls, + num_ctrl_qubits: Optional[int] = None, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create a new MCX instance. Depending on the number of controls and which mode of the MCX, this creates an @@ -868,7 +916,13 @@ def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None): return gate return super().__new__(cls) - def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None, _name="mcx"): + def __init__( + self, + num_ctrl_qubits: int, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + _name="mcx", + ): """Create new MCX gate.""" num_ancilla_qubits = self.__class__.get_num_ancilla_qubits(num_ctrl_qubits) super().__init__( @@ -886,7 +940,7 @@ def inverse(self): return MCXGate(num_ctrl_qubits=self.num_ctrl_qubits, ctrl_state=self.ctrl_state) @staticmethod - def get_num_ancilla_qubits(num_ctrl_qubits, mode="noancilla"): + def get_num_ancilla_qubits(num_ctrl_qubits: int, mode: str = "noancilla") -> int: """Get the number of required ancilla qubits without instantiating the class. This staticmethod might be necessary to check the number of ancillas before @@ -915,7 +969,12 @@ def num_ancilla_qubits(self): """The number of ancilla qubits.""" return self.__class__.get_num_ancilla_qubits(self.num_ctrl_qubits) - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a multi-controlled-X gate with more control lines. Args: @@ -943,7 +1002,12 @@ class MCXGrayCode(MCXGate): This delegates the implementation to the MCU1 gate, since :math:`X = H \cdot U1(\pi) \cdot H`. """ - def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None): + def __new__( + cls, + num_ctrl_qubits: Optional[int] = None, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Create a new MCXGrayCode instance""" # if 1 to 4 control qubits, create explicit gates explicit = {1: CXGate, 2: CCXGate, 3: C3XGate, 4: C4XGate} @@ -955,7 +1019,12 @@ def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None): return gate return super().__new__(cls) - def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None): + def __init__( + self, + num_ctrl_qubits: int, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name="mcx_gray") def inverse(self): @@ -984,11 +1053,16 @@ class MCXRecursive(MCXGate): for these we have a concrete implementation that do not require ancillas. """ - def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None): + def __init__( + self, + num_ctrl_qubits: int, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name="mcx_recursive") @staticmethod - def get_num_ancilla_qubits(num_ctrl_qubits, mode="recursion"): + def get_num_ancilla_qubits(num_ctrl_qubits: int, mode: str = "recursion"): """Get the number of required ancilla qubits.""" return MCXGate.get_num_ancilla_qubits(num_ctrl_qubits, mode) @@ -1043,10 +1117,10 @@ class MCXVChain(MCXGate): def __new__( cls, - num_ctrl_qubits=None, - dirty_ancillas=False, # pylint: disable=unused-argument - label=None, - ctrl_state=None, + num_ctrl_qubits: Optional[int] = None, + dirty_ancillas: bool = False, # pylint: disable=unused-argument + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, ): """Create a new MCX instance. @@ -1054,7 +1128,13 @@ def __new__( """ return super().__new__(cls, num_ctrl_qubits, label=label, ctrl_state=ctrl_state) - def __init__(self, num_ctrl_qubits, dirty_ancillas=False, label=None, ctrl_state=None): + def __init__( + self, + num_ctrl_qubits: int, + dirty_ancillas: bool = False, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name="mcx_vchain") self._dirty_ancillas = dirty_ancillas @@ -1067,7 +1147,7 @@ def inverse(self): ) @staticmethod - def get_num_ancilla_qubits(num_ctrl_qubits, mode="v-chain"): + def get_num_ancilla_qubits(num_ctrl_qubits: int, mode: str = "v-chain"): """Get the number of required ancilla qubits.""" return MCXGate.get_num_ancilla_qubits(num_ctrl_qubits, mode) diff --git a/qiskit/circuit/library/standard_gates/y.py b/qiskit/circuit/library/standard_gates/y.py index a08d3393a04e..b356f19a8aad 100644 --- a/qiskit/circuit/library/standard_gates/y.py +++ b/qiskit/circuit/library/standard_gates/y.py @@ -12,6 +12,7 @@ """Y and CY gates.""" +from typing import Optional, Union import numpy from qiskit.qasm import pi @@ -64,7 +65,7 @@ class YGate(Gate): |1\rangle \rightarrow -i|0\rangle """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new Y gate.""" super().__init__("y", 1, [], label=label) @@ -81,7 +82,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-Y gate. One control returns a CY gate. @@ -166,7 +172,7 @@ class CYGate(ControlledGate): _matrix1 = numpy.array([[1, 0, 0, 0], [0, 0, 0, -1j], [0, 0, 1, 0], [0, 1j, 0, 0]]) _matrix0 = numpy.array([[0, 0, -1j, 0], [0, 1, 0, 0], [1j, 0, 0, 0], [0, 0, 0, 1]]) - def __init__(self, label=None, ctrl_state=None): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CY gate.""" super().__init__( "cy", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=YGate() diff --git a/qiskit/circuit/library/standard_gates/z.py b/qiskit/circuit/library/standard_gates/z.py index 9c6d4475fdd9..ee0dbc180549 100644 --- a/qiskit/circuit/library/standard_gates/z.py +++ b/qiskit/circuit/library/standard_gates/z.py @@ -12,6 +12,7 @@ """Z and CZ gates.""" +from typing import Optional, Union import numpy from qiskit.qasm import pi from qiskit.circuit.controlledgate import ControlledGate @@ -62,7 +63,7 @@ class ZGate(Gate): |1\rangle \rightarrow -|1\rangle """ - def __init__(self, label=None): + def __init__(self, label: Optional[str] = None): """Create new Z gate.""" super().__init__("z", 1, [], label=label) @@ -79,7 +80,12 @@ def _define(self): self.definition = qc - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ): """Return a (multi-)controlled-Z gate. One control returns a CZ gate. @@ -138,7 +144,7 @@ class CZGate(ControlledGate): the target qubit if the control qubit is in the :math:`|1\rangle` state. """ - def __init__(self, label=None, ctrl_state=None): + def __init__(self, label: Optional[str] = None, ctrl_state: Optional[Union[str, int]] = None): """Create new CZ gate.""" super().__init__( "cz", 2, [], label=label, num_ctrl_qubits=1, ctrl_state=ctrl_state, base_gate=ZGate() diff --git a/qiskit/circuit/measure.py b/qiskit/circuit/measure.py index ef6acaf83c0c..7282d1873d85 100644 --- a/qiskit/circuit/measure.py +++ b/qiskit/circuit/measure.py @@ -14,7 +14,6 @@ Quantum measurement in the computational basis. """ from qiskit.circuit.instruction import Instruction -from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.exceptions import CircuitError @@ -37,23 +36,3 @@ def broadcast_arguments(self, qargs, cargs): yield qarg, [each_carg] else: raise CircuitError("register size error") - - -def measure(self, qubit, cbit): - """Measure quantum bit into classical bit (tuples). - - Args: - qubit (QuantumRegister|list|tuple): quantum register - cbit (ClassicalRegister|list|tuple): classical register - - Returns: - qiskit.Instruction: the attached measure instruction. - - Raises: - CircuitError: if qubit is not in this circuit or bad format; - if cbit is not in this circuit or not creg. - """ - return self.append(Measure(), [qubit], [cbit]) - - -QuantumCircuit.measure = measure diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 4fc12468ef1b..c90c59166d78 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -29,6 +29,9 @@ HAS_SYMENGINE = False +# This type is redefined at the bottom to insert the full reference to "ParameterExpression", so it +# can safely be used by runtime type-checkers like Sphinx. Mypy does not need this because it +# handles the references by static analysis. ParameterValueType = Union["ParameterExpression", float] @@ -515,3 +518,8 @@ def is_real(self): else: return False return True + + +# Redefine the type so external imports get an evaluated reference; Sphinx needs this to understand +# the type hints. +ParameterValueType = Union[ParameterExpression, float] diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 80e6956015fc..fc310bcfd6fa 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -17,10 +17,22 @@ import copy import itertools import functools -import numbers import multiprocessing as mp from collections import OrderedDict, defaultdict -from typing import Union +from typing import ( + Union, + Optional, + List, + Dict, + Tuple, + Type, + TypeVar, + Sequence, + Callable, + Mapping, + Set, +) +import typing import numpy as np from qiskit.exceptions import QiskitError, MissingOptionalLibraryError from qiskit.utils.multiprocessing import is_main_process @@ -31,7 +43,7 @@ from qiskit.qasm.exceptions import QasmError from qiskit.circuit.exceptions import CircuitError from qiskit.utils.deprecation import deprecate_function -from .parameterexpression import ParameterExpression +from .parameterexpression import ParameterExpression, ParameterValueType from .quantumregister import QuantumRegister, Qubit, AncillaRegister, AncillaQubit from .classicalregister import ClassicalRegister, Clbit from .parametertable import ParameterTable, ParameterView @@ -41,6 +53,8 @@ from .bit import Bit from .quantumcircuitdata import QuantumCircuitData from .delay import Delay +from .measure import Measure +from .reset import Reset try: import pygments @@ -53,6 +67,39 @@ HAS_PYGMENTS = False +# The following types are not marked private to avoid leaking this "private/public" abstraction out +# into the documentation. They are not imported by circuit.__init__, nor are they meant to be. + +# Arbitrary type variables for marking up generics. +S = TypeVar("S") +T = TypeVar("T") + +# Type of the elements of QuantumCircuit._data. +DataElement = Tuple[Instruction, List[Qubit], List[Clbit]] + +# Types that can be coerced to a valid Qubit specifier in a circuit. +QubitSpecifier = Union[ + Qubit, + QuantumRegister, + int, + slice, + Sequence[Union[Qubit, int]], +] + +# Types that can be coerced to a valid Clbit specifier in a circuit. +ClbitSpecifier = Union[ + Clbit, + ClassicalRegister, + int, + slice, + Sequence[Union[Clbit, int]], +] + +# Generic type which is either :obj:`~Qubit` or :obj:`~Clbit`, used to specify types of functions +# which operate on either type of bit, but not both at the same time. +BitType = TypeVar("BitType", Qubit, Clbit) + + class QuantumCircuit: """Create a new circuit. @@ -149,7 +196,13 @@ class QuantumCircuit: header = "OPENQASM 2.0;" extension_lib = 'include "qelib1.inc";' - def __init__(self, *regs, name=None, global_phase=0, metadata=None): + def __init__( + self, + *regs: Union[Register, int, Sequence[Bit]], + name: Optional[str] = None, + global_phase: ParameterValueType = 0, + metadata: Optional[Dict] = None, + ): if any(not isinstance(reg, (list, QuantumRegister, ClassicalRegister)) for reg in regs): # check if inputs are integers, but also allow e.g. 2.0 @@ -201,7 +254,7 @@ def __init__(self, *regs, name=None, global_phase=0, metadata=None): self._parameters = None self._layout = None - self._global_phase = 0 + self._global_phase: ParameterValueType = 0 self.global_phase = global_phase self.duration = None @@ -211,7 +264,7 @@ def __init__(self, *regs, name=None, global_phase=0, metadata=None): self._metadata = metadata @property - def data(self): + def data(self) -> QuantumCircuitData: """Return the circuit data (instructions and context). Returns: @@ -223,27 +276,10 @@ def data(self): """ return QuantumCircuitData(self) - @property - def calibrations(self): - """Return calibration dictionary. - - The custom pulse definition of a given gate is of the form - {'gate_name': {(qubits, params): schedule}} - """ - return dict(self._calibrations) - - @calibrations.setter - def calibrations(self, calibrations): - """Set the circuit calibration data from a dictionary of calibration definition. - - Args: - calibrations (dict): A dictionary of input in the format - {'gate_name': {(qubits, gate_params): schedule}} - """ - self._calibrations = defaultdict(dict, calibrations) - @data.setter - def data(self, data_input): + def data( + self, data_input: List[Tuple[Instruction, List[QubitSpecifier], List[ClbitSpecifier]]] + ): """Sets the circuit data from a list of instructions and context. Args: @@ -263,7 +299,26 @@ def data(self, data_input): self.append(inst, qargs, cargs) @property - def metadata(self): + def calibrations(self) -> dict: + """Return calibration dictionary. + + The custom pulse definition of a given gate is of the form + {'gate_name': {(qubits, params): schedule}} + """ + return dict(self._calibrations) + + @calibrations.setter + def calibrations(self, calibrations: dict): + """Set the circuit calibration data from a dictionary of calibration definition. + + Args: + calibrations (dict): A dictionary of input in the format + {'gate_name': {(qubits, gate_params): schedule}} + """ + self._calibrations = defaultdict(dict, calibrations) + + @property + def metadata(self) -> dict: """The user provided metadata associated with the circuit The metadata for the circuit is a user provided ``dict`` of metadata @@ -276,16 +331,16 @@ def metadata(self): return self._metadata @metadata.setter - def metadata(self, metadata): + def metadata(self, metadata: Optional[dict]): """Update the circuit metadata""" if not isinstance(metadata, dict) and metadata is not None: raise TypeError("Only a dictionary or None is accepted for circuit metadata") self._metadata = metadata - def __str__(self): + def __str__(self) -> str: return str(self.draw(output="text")) - def __eq__(self, other): + def __eq__(self, other) -> bool: if not isinstance(other, QuantumCircuit): return False @@ -299,17 +354,17 @@ def _increment_instances(cls): cls.instances += 1 @classmethod - def cls_instances(cls): + def cls_instances(cls) -> int: """Return the current number of instances of this class, useful for auto naming.""" return cls.instances @classmethod - def cls_prefix(cls): + def cls_prefix(cls) -> str: """Return the prefix to use for auto naming.""" return cls.prefix - def _name_update(self): + def _name_update(self) -> None: """update name of instance using instance number""" if not is_main_process(): pid_name = f"-{mp.current_process().pid}" @@ -318,7 +373,7 @@ def _name_update(self): self.name = f"{self._base_name}-{self.cls_instances()}{pid_name}" - def has_register(self, register): + def has_register(self, register: Register) -> bool: """ Test if this circuit has the register r. @@ -335,7 +390,7 @@ def has_register(self, register): has_reg = True return has_reg - def reverse_ops(self): + def reverse_ops(self) -> "QuantumCircuit": """Reverse the circuit by reversing the order of instructions. This is done by recursively reversing all instructions. @@ -371,7 +426,7 @@ def reverse_ops(self): reverse_circ.unit = self.unit return reverse_circ - def reverse_bits(self): + def reverse_bits(self) -> "QuantumCircuit": """Return a circuit with the opposite order of wires. The circuit is "vertically" flipped. If a circuit is @@ -419,7 +474,7 @@ def reverse_bits(self): circ._append(inst, new_qargs, new_cargs) return circ - def inverse(self): + def inverse(self) -> "QuantumCircuit": """Invert (take adjoint of) this circuit. This is done by recursively inverting all gates. @@ -459,7 +514,7 @@ def inverse(self): inverse_circ._append(inst.inverse(), qargs, cargs) return inverse_circ - def repeat(self, reps): + def repeat(self, reps: int) -> "QuantumCircuit": """Repeat this circuit ``reps`` times. Args: @@ -476,7 +531,7 @@ def repeat(self, reps): # is actually `reps` times this circuit, and it is currently much faster than `compose`. if reps > 0: try: # try to append as gate if possible to not disallow to_gate - inst = self.to_gate() + inst: Instruction = self.to_gate() except QiskitError: inst = self.to_instruction() for _ in range(reps): @@ -484,7 +539,7 @@ def repeat(self, reps): return repeated_circ - def power(self, power, matrix_power=False): + def power(self, power: float, matrix_power: bool = False) -> "QuantumCircuit": """Raise this circuit to the power of ``power``. If ``power`` is a positive integer and ``matrix_power`` is ``False``, this implementation @@ -492,7 +547,7 @@ def power(self, power, matrix_power=False): computed to calculate the matrix power. Args: - power (int): The power to raise this circuit to. + power (float): The power to raise this circuit to. matrix_power (bool): If True, the circuit is converted to a matrix and then the matrix power is computed. If False, and ``power`` is a positive integer, the implementation defaults to ``repeat``. @@ -503,7 +558,7 @@ def power(self, power, matrix_power=False): Returns: QuantumCircuit: A circuit implementing this circuit raised to the power of ``power``. """ - if power >= 0 and isinstance(power, numbers.Integral) and not matrix_power: + if power >= 0 and isinstance(power, int) and not matrix_power: return self.repeat(power) # attempt conversion to gate @@ -527,7 +582,12 @@ def power(self, power, matrix_power=False): power_circuit.append(gate.power(power), list(range(gate.num_qubits))) return power_circuit - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): + def control( + self, + num_ctrl_qubits: int = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> "QuantumCircuit": """Control this circuit on ``num_ctrl_qubits`` qubits. Args: @@ -565,7 +625,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): "Use the compose() method which is more flexible w.r.t " "circuit register compatibility." ) - def combine(self, rhs): + def combine(self, rhs: "QuantumCircuit") -> "QuantumCircuit": """DEPRECATED - Returns rhs appended to self if self contains compatible registers. Two circuits are compatible if they contain the same registers @@ -617,7 +677,7 @@ def combine(self, rhs): "compose() (potentially with the inplace=True argument) and tensor() " "methods which are more flexible w.r.t circuit register compatibility." ) - def extend(self, rhs): + def extend(self, rhs: "QuantumCircuit") -> "QuantumCircuit": """DEPRECATED - Append QuantumCircuit to the RHS if it contains compatible registers. Two circuits are compatible if they contain the same registers @@ -666,14 +726,23 @@ def extend(self, rhs): return self - def compose(self, other, qubits=None, clbits=None, front=False, inplace=False, wrap=False): + def compose( + self, + other: Union["QuantumCircuit", Instruction], + qubits: Optional[Sequence[Union[Qubit, int]]] = None, + clbits: Optional[Sequence[Union[Clbit, int]]] = None, + front: bool = False, + inplace: bool = False, + wrap: bool = False, + ) -> Optional["QuantumCircuit"]: """Compose circuit with ``other`` circuit or instruction, optionally permuting wires. ``other`` can be narrower or of equal width to ``self``. Args: - other (qiskit.circuit.Instruction or QuantumCircuit or BaseOperator): - (sub)circuit to compose onto self. + other (qiskit.circuit.Instruction or QuantumCircuit): + (sub)circuit or instruction to compose onto self. If not a :obj:`.QuantumCircuit`, + this can be anything that :obj:`.append` will accept. qubits (list[Qubit|int]): qubits of self to compose onto. clbits (list[Clbit|int]): clbits of self to compose onto. front (bool): If True, front composition will be performed (not implemented yet). @@ -813,7 +882,7 @@ def compose(self, other, qubits=None, clbits=None, front=False, inplace=False, w return dest - def tensor(self, other, inplace=False): + def tensor(self, other: "QuantumCircuit", inplace: bool = False) -> Optional["QuantumCircuit"]: """Tensor ``self`` with ``other``. Remember that in the little-endian convention the leftmost operation will be at the bottom @@ -903,21 +972,21 @@ def tensor(self, other, inplace=False): return dest @property - def qubits(self): + def qubits(self) -> List[Qubit]: """ Returns a list of quantum bits in the order that the registers were added. """ return self._qubits @property - def clbits(self): + def clbits(self) -> List[Clbit]: """ Returns a list of classical bits in the order that the registers were added. """ return self._clbits @property - def ancillas(self): + def ancillas(self) -> List[AncillaQubit]: """ Returns a list of ancilla bits in the order that the registers were added. """ @@ -928,7 +997,7 @@ def ancillas(self): "Use the compose() method which is more flexible w.r.t " "circuit register compatibility." ) - def __add__(self, rhs): + def __add__(self, rhs: "QuantumCircuit") -> "QuantumCircuit": """Overload + to implement self.combine.""" return self.combine(rhs) @@ -937,47 +1006,54 @@ def __add__(self, rhs): "compose() (potentially with the inplace=True argument) and tensor() " "methods which are more flexible w.r.t circuit register compatibility." ) - def __iadd__(self, rhs): + def __iadd__(self, rhs: "QuantumCircuit") -> "QuantumCircuit": """Overload += to implement self.extend.""" return self.extend(rhs) - def __and__(self, rhs): + def __and__(self, rhs: "QuantumCircuit") -> "QuantumCircuit": """Overload & to implement self.compose.""" return self.compose(rhs) - def __iand__(self, rhs): + def __iand__(self, rhs: "QuantumCircuit") -> "QuantumCircuit": """Overload &= to implement self.compose in place.""" self.compose(rhs, inplace=True) return self - def __xor__(self, top): + def __xor__(self, top: "QuantumCircuit") -> "QuantumCircuit": """Overload ^ to implement self.tensor.""" return self.tensor(top) - def __ixor__(self, top): + def __ixor__(self, top: "QuantumCircuit") -> "QuantumCircuit": """Overload ^= to implement self.tensor in place.""" self.tensor(top, inplace=True) return self - def __len__(self): + def __len__(self) -> int: """Return number of operations in circuit.""" return len(self._data) + @typing.overload + def __getitem__(self, item: int) -> DataElement: + ... + + @typing.overload + def __getitem__(self, item: slice) -> List[DataElement]: + ... + def __getitem__(self, item): """Return indexed operation.""" return self._data[item] @staticmethod - def cast(value, _type): + def cast(value: S, type_: Callable[..., T]) -> Union[S, T]: """Best effort to cast value to type. Otherwise, returns the value.""" try: - return _type(value) + return type_(value) except (ValueError, TypeError): return value @staticmethod - def _bit_argument_conversion(bit_representation, in_array): - ret = None + def _bit_argument_conversion(bit_representation, in_array: Sequence[BitType]) -> List[BitType]: try: if isinstance(bit_representation, Bit): # circuit.h(qr[0]) -> circuit.h([qr[0]]) @@ -1016,7 +1092,7 @@ def _bit_argument_conversion(bit_representation, in_array): ) from ex return ret - def qbit_argument_conversion(self, qubit_representation): + def qbit_argument_conversion(self, qubit_representation: QubitSpecifier) -> List[Qubit]: """ Converts several qubit representations (such as indexes, range, etc.) into a list of qubits. @@ -1025,11 +1101,11 @@ def qbit_argument_conversion(self, qubit_representation): qubit_representation (Object): representation to expand Returns: - List(tuple): Where each tuple is a qubit. + List(Qubit): the resolved instances of the qubits. """ return QuantumCircuit._bit_argument_conversion(qubit_representation, self.qubits) - def cbit_argument_conversion(self, clbit_representation): + def cbit_argument_conversion(self, clbit_representation: ClbitSpecifier) -> List[Clbit]: """ Converts several classical bit representations (such as indexes, range, etc.) into a list of classical bits. @@ -1042,7 +1118,12 @@ def cbit_argument_conversion(self, clbit_representation): """ return QuantumCircuit._bit_argument_conversion(clbit_representation, self.clbits) - def append(self, instruction, qargs=None, cargs=None): + def append( + self, + instruction: Instruction, + qargs: Optional[Sequence[QubitSpecifier]] = None, + cargs: Optional[Sequence[ClbitSpecifier]] = None, + ) -> InstructionSet: """Append one or more instructions to the end of the circuit, modifying the circuit in place. Expands qargs and cargs. @@ -1086,14 +1167,16 @@ def append(self, instruction, qargs=None, cargs=None): instructions.add(self._append(instruction, qarg, carg), qarg, carg) return instructions - def _append(self, instruction, qargs, cargs): + def _append( + self, instruction: Instruction, qargs: Sequence[Qubit], cargs: Sequence[Clbit] + ) -> Instruction: """Append an instruction to the end of the circuit, modifying the circuit in place. Args: - instruction (Instruction or Operator): Instruction instance to append - qargs (list(tuple)): qubits to attach instruction to - cargs (list(tuple)): clbits to attach instruction to + instruction: Instruction instance to append + qargs: qubits to attach instruction to + cargs: clbits to attach instruction to Returns: Instruction: a handle to the instruction that was just added @@ -1122,7 +1205,7 @@ def _append(self, instruction, qargs, cargs): return instruction - def _update_parameter_table(self, instruction): + def _update_parameter_table(self, instruction: Instruction) -> Instruction: for param_index, param in enumerate(instruction.params): if isinstance(param, ParameterExpression): @@ -1146,13 +1229,18 @@ def _update_parameter_table(self, instruction): return instruction - def _check_dup_param_spec(self, parameter_spec_list, instruction, param_index): + def _check_dup_param_spec( + self, + parameter_spec_list: Sequence[Tuple[Instruction, int]], + instruction: Instruction, + param_index: int, + ) -> bool: for spec in parameter_spec_list: if spec[0] is instruction and spec[1] == param_index: return True return False - def add_register(self, *regs): + def add_register(self, *regs: Union[Register, int, Sequence[Bit]]) -> None: """Add registers.""" if not regs: return @@ -1196,7 +1284,7 @@ def add_register(self, *regs): else: raise CircuitError("expected a register") - def add_bits(self, bits): + def add_bits(self, bits: Sequence[Bit]) -> None: """Add Bits to the circuit.""" duplicate_bits = set(self.qubits + self.clbits).intersection(bits) if duplicate_bits: @@ -1220,27 +1308,31 @@ def add_bits(self, bits): "AncillaQubit, but was passed {}".format(bit) ) - def _check_dups(self, qubits): + def _check_dups(self, qubits: Sequence[Qubit]) -> None: """Raise exception if list of qubits contains duplicates.""" squbits = set(qubits) if len(squbits) != len(qubits): raise CircuitError("duplicate qubit arguments") - def _check_qargs(self, qargs): + def _check_qargs(self, qargs: Sequence[Qubit]) -> None: """Raise exception if a qarg is not in this circuit or bad format.""" if not all(isinstance(i, Qubit) for i in qargs): raise CircuitError("qarg is not a Qubit") if not set(qargs).issubset(self._qubit_set): raise CircuitError("qargs not in this circuit") - def _check_cargs(self, cargs): + def _check_cargs(self, cargs: Sequence[Clbit]) -> None: """Raise exception if clbit is not in this circuit or bad format.""" if not all(isinstance(i, Clbit) for i in cargs): raise CircuitError("carg is not a Clbit") if not set(cargs).issubset(self._clbit_set): raise CircuitError("cargs not in this circuit") - def to_instruction(self, parameter_map=None, label=None): + def to_instruction( + self, + parameter_map: Optional[Dict[Parameter, ParameterValueType]] = None, + label: Optional[str] = None, + ) -> Instruction: """Create an Instruction out of this circuit. Args: @@ -1258,7 +1350,11 @@ def to_instruction(self, parameter_map=None, label=None): return circuit_to_instruction(self, parameter_map, label=label) - def to_gate(self, parameter_map=None, label=None): + def to_gate( + self, + parameter_map: Optional[Dict[Parameter, ParameterValueType]] = None, + label: Optional[str] = None, + ) -> Gate: """Create a Gate out of this circuit. Args: @@ -1276,7 +1372,12 @@ def to_gate(self, parameter_map=None, label=None): return circuit_to_gate(self, parameter_map, label=label) - def decompose(self, gates_to_decompose=None): + def decompose( + self, + gates_to_decompose: Optional[ + Union[Type[Gate], Sequence[Type[Gate]], Sequence[str], str] + ] = None, + ) -> "QuantumCircuit": """Call a decomposition pass on this circuit, to decompose one level (shallow decompose). @@ -1296,7 +1397,7 @@ def decompose(self, gates_to_decompose=None): decomposed_dag = pass_.run(circuit_to_dag(self)) return dag_to_circuit(decomposed_dag) - def _check_compatible_regs(self, rhs): + def _check_compatible_regs(self, rhs: "QuantumCircuit") -> None: """Raise exception if the circuits are defined on incompatible registers""" list1 = self.qregs + self.cregs list2 = rhs.qregs + rhs.cregs @@ -1309,7 +1410,12 @@ def _check_compatible_regs(self, rhs): f" registers {element1} and {element2} not compatible" ) - def qasm(self, formatted=False, filename=None, encoding=None): + def qasm( + self, + formatted: bool = False, + filename: Optional[str] = None, + encoding: Optional[str] = None, + ) -> Optional[str]: """Return OpenQASM string. Args: @@ -1477,21 +1583,23 @@ def qasm(self, formatted=False, filename=None, encoding=None): def draw( self, - output=None, - scale=None, - filename=None, - style=None, - interactive=False, - plot_barriers=True, - reverse_bits=False, - justify=None, - vertical_compression="medium", - idle_wires=True, - with_layout=True, - fold=None, - ax=None, - initial_state=False, - cregbundle=True, + output: Optional[str] = None, + scale: Optional[float] = None, + filename: Optional[str] = None, + style: Optional[Union[dict, str]] = None, + interactive: bool = False, + plot_barriers: bool = True, + reverse_bits: bool = False, + justify: Optional[str] = None, + vertical_compression: Optional[str] = "medium", + idle_wires: bool = True, + with_layout: bool = True, + fold: Optional[int] = None, + # The type of ax is matplotlib.axes.Axes, but this is not a fixed dependency, so cannot be + # safely forward-referenced. + ax: Optional[typing.Any] = None, + initial_state: bool = False, + cregbundle: bool = True, ): """Draw the quantum circuit. Use the output parameter to choose the drawing format: @@ -1626,7 +1734,7 @@ def draw( cregbundle=cregbundle, ) - def size(self): + def size(self) -> int: """Returns total number of gate operations in circuit. Returns: @@ -1638,7 +1746,7 @@ def size(self): gate_ops += 1 return gate_ops - def depth(self): + def depth(self) -> int: """Return circuit depth (i.e., length of critical path). This does not include compiler or simulator directives such as 'barrier' or 'snapshot'. @@ -1708,7 +1816,7 @@ def depth(self): return max(op_stack) - def width(self): + def width(self) -> int: """Return number of qubits plus clbits in circuit. Returns: @@ -1718,32 +1826,35 @@ def width(self): return len(self.qubits) + len(self.clbits) @property - def num_qubits(self): + def num_qubits(self) -> int: """Return number of qubits.""" return len(self.qubits) @property - def num_ancillas(self): + def num_ancillas(self) -> int: """Return the number of ancilla qubits.""" return len(self.ancillas) @property - def num_clbits(self): + def num_clbits(self) -> int: """Return number of classical bits.""" return len(self.clbits) - def count_ops(self): + # The stringified return type is because OrderedDict can't be subscripted before Python 3.9, and + # typing.OrderedDict wasn't added until 3.7.2. It can be turned into a proper type once 3.6 + # support is dropped. + def count_ops(self) -> "OrderedDict[Instruction, int]": """Count each operation kind in the circuit. Returns: OrderedDict: a breakdown of how many operations of each kind, sorted by amount. """ - count_ops = {} + count_ops: Dict[Instruction, int] = {} for instr, _, _ in self._data: count_ops[instr.name] = count_ops.get(instr.name, 0) + 1 return OrderedDict(sorted(count_ops.items(), key=lambda kv: kv[1], reverse=True)) - def num_nonlocal_gates(self): + def num_nonlocal_gates(self) -> int: """Return number of non-local gates (i.e. involving 2+ qubits). Conditional nonlocal gates are also included. @@ -1754,7 +1865,7 @@ def num_nonlocal_gates(self): multi_qubit_gates += 1 return multi_qubit_gates - def get_instructions(self, name): + def get_instructions(self, name: str) -> List[DataElement]: """Get instructions matching name. Args: @@ -1765,7 +1876,7 @@ def get_instructions(self, name): """ return [match for match in self._data if match[0].name == name] - def num_connected_components(self, unitary_only=False): + def num_connected_components(self, unitary_only: bool = False) -> int: """How many non-entangled subcircuits can the circuit be factored to. Args: @@ -1840,13 +1951,13 @@ def num_connected_components(self, unitary_only=False): break return num_sub_graphs - def num_unitary_factors(self): + def num_unitary_factors(self) -> int: """Computes the number of tensor factors in the unitary (quantum) part of the circuit only. """ return self.num_connected_components(unitary_only=True) - def num_tensor_factors(self): + def num_tensor_factors(self) -> int: """Computes the number of tensor factors in the unitary (quantum) part of the circuit only. @@ -1857,7 +1968,7 @@ def num_tensor_factors(self): """ return self.num_unitary_factors() - def copy(self, name=None): + def copy(self, name: Optional[str] = None) -> "QuantumCircuit": """Copy the circuit. Args: @@ -1901,7 +2012,7 @@ def copy(self, name=None): cpy.name = name return cpy - def _create_creg(self, length, name): + def _create_creg(self, length: int, name: str) -> ClassicalRegister: """Creates a creg, checking if ClassicalRegister with same name exists""" if name in [creg.name for creg in self.cregs]: save_prefix = ClassicalRegister.prefix @@ -1912,7 +2023,7 @@ def _create_creg(self, length, name): new_creg = ClassicalRegister(length, name) return new_creg - def _create_qreg(self, length, name): + def _create_qreg(self, length: int, name: str) -> QuantumRegister: """Creates a qreg, checking if QuantumRegister with same name exists""" if name in [qreg.name for qreg in self.qregs]: save_prefix = QuantumRegister.prefix @@ -1923,7 +2034,33 @@ def _create_qreg(self, length, name): new_qreg = QuantumRegister(length, name) return new_qreg - def measure_active(self, inplace=True): + def reset(self, qubit: QubitSpecifier) -> InstructionSet: + """Reset the quantum bit(s) to their default state. + + Args: + qubit: qubit(s) to reset. + + Returns: + qiskit.circuit.InstructionSet: handle to the added instruction. + """ + return self.append(Reset(), [qubit], []) + + def measure(self, qubit: QubitSpecifier, cbit: ClbitSpecifier) -> InstructionSet: + """Measure quantum bit into classical bit (tuples). + + Args: + qubit: qubit to measure. + cbit: classical bit to place the measurement in. + + Returns: + qiskit.circuit.InstructionSet: handle to the added instructions. + + Raises: + CircuitError: if arguments have bad format. + """ + return self.append(Measure(), [qubit], [cbit]) + + def measure_active(self, inplace: bool = True) -> Optional["QuantumCircuit"]: """Adds measurement to all non-idle qubits. Creates a new ClassicalRegister with a size equal to the number of non-idle qubits being measured. @@ -1953,7 +2090,7 @@ def measure_active(self, inplace=True): else: return None - def measure_all(self, inplace=True): + def measure_all(self, inplace: bool = True) -> Optional["QuantumCircuit"]: """Adds measurement to all qubits. Creates a new ClassicalRegister with a size equal to the number of qubits being measured. @@ -1980,7 +2117,7 @@ def measure_all(self, inplace=True): else: return None - def remove_final_measurements(self, inplace=True): + def remove_final_measurements(self, inplace: bool = True) -> Optional["QuantumCircuit"]: """Removes final measurement on all qubits if they are present. Deletes the ClassicalRegister that was used to store the values from these measurements if it is idle. @@ -2024,7 +2161,7 @@ def remove_final_measurements(self, inplace=True): return None @staticmethod - def from_qasm_file(path): + def from_qasm_file(path: str) -> "QuantumCircuit": """Take in a QASM file and generate a QuantumCircuit object. Args: @@ -2036,7 +2173,7 @@ def from_qasm_file(path): return _circuit_from_qasm(qasm) @staticmethod - def from_qasm_str(qasm_str): + def from_qasm_str(qasm_str: str) -> "QuantumCircuit": """Take in a QASM string and generate a QuantumCircuit object. Args: @@ -2048,12 +2185,12 @@ def from_qasm_str(qasm_str): return _circuit_from_qasm(qasm) @property - def global_phase(self): + def global_phase(self) -> ParameterValueType: """Return the global phase of the circuit in radians.""" return self._global_phase @global_phase.setter - def global_phase(self, angle): + def global_phase(self, angle: ParameterValueType): """Set the phase of the circuit. Args: @@ -2070,7 +2207,7 @@ def global_phase(self, angle): self._global_phase = angle % (2 * np.pi) @property - def parameters(self): + def parameters(self) -> ParameterView: """Convenience function to get the parameters defined in the parameter table.""" # parameters from gates if self._parameters is None: @@ -2081,11 +2218,11 @@ def parameters(self): return ParameterView(self._parameters) @property - def num_parameters(self): + def num_parameters(self) -> int: """Convenience function to get the number of parameter objects in the circuit.""" return len(self._unsorted_parameters()) - def _unsorted_parameters(self): + def _unsorted_parameters(self) -> Set[Parameter]: """Efficiently get all parameters in the circuit, without any sorting overhead.""" parameters = set(self._parameter_table) if isinstance(self.global_phase, ParameterExpression): @@ -2093,7 +2230,11 @@ def _unsorted_parameters(self): return parameters - def assign_parameters(self, parameters, inplace=False): + def assign_parameters( + self, + parameters: Union[Mapping[Parameter, ParameterValueType], Sequence[ParameterValueType]], + inplace: bool = False, + ) -> Optional["QuantumCircuit"]: """Assign parameters to new parameters or values. The keys of the parameter dictionary must be Parameter instances in the current circuit. The @@ -2198,7 +2339,9 @@ def assign_parameters(self, parameters, inplace=False): bound_circuit._assign_parameter(self.parameters[i], value) return None if inplace else bound_circuit - def bind_parameters(self, values): + def bind_parameters( + self, values: Union[Mapping[Parameter, float], Sequence[float]] + ) -> "QuantumCircuit": """Assign numeric parameters to values yielding a new circuit. To assign new Parameter objects or bind the values in-place, without yielding a new @@ -2227,8 +2370,10 @@ def bind_parameters(self, values): ) return self.assign_parameters(values) - def _unroll_param_dict(self, value_dict): - unrolled_value_dict = {} + def _unroll_param_dict( + self, value_dict: Mapping[Parameter, ParameterValueType] + ) -> Dict[Parameter, ParameterValueType]: + unrolled_value_dict: Dict[Parameter, ParameterValueType] = {} for (param, value) in value_dict.items(): if isinstance(param, ParameterVector): if not len(param) == len(value): @@ -2243,7 +2388,7 @@ def _unroll_param_dict(self, value_dict): unrolled_value_dict[param] = value return unrolled_value_dict - def _assign_parameter(self, parameter, value): + def _assign_parameter(self, parameter: Parameter, value: ParameterValueType) -> None: """Update this circuit where instances of ``parameter`` are replaced by ``value``, which can be either a numeric value or a new parameter expression. @@ -2284,11 +2429,14 @@ def _assign_parameter(self, parameter, value): self._parameters = None self._assign_calibration_parameters(parameter, value) - def _assign_calibration_parameters(self, parameter, value): + def _assign_calibration_parameters( + self, parameter: Parameter, value: ParameterValueType + ) -> None: """Update parameterized pulse gate calibrations, if there are any which contain ``parameter``. This updates the calibration mapping as well as the gate definition ``Schedule``s, which also may contain ``parameter``. """ + new_param: ParameterValueType for cals in self.calibrations.values(): for (qubit, cal_params), schedule in copy.copy(cals).items(): if any( @@ -2308,7 +2456,9 @@ def _assign_calibration_parameters(self, parameter, value): schedule.assign_parameters({parameter: value}) cals[(qubit, tuple(new_cal_params))] = schedule - def _rebind_definition(self, instruction, parameter, value): + def _rebind_definition( + self, instruction: Instruction, parameter: Parameter, value: ParameterValueType + ) -> None: if instruction._definition: for op, _, _ in instruction._definition: for idx, param in enumerate(op.params): @@ -2319,11 +2469,16 @@ def _rebind_definition(self, instruction, parameter, value): op.params[idx] = param.bind({parameter: value}) self._rebind_definition(op, parameter, value) - def barrier(self, *qargs): - """Apply :class:`~qiskit.circuit.Barrier`. If qargs is None, applies to all.""" + def barrier(self, *qargs: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.Barrier`. If qargs is empty, applies to all qubits in the + circuit. + + Returns: + qiskit.circuit.InstructionSet: handle to the added instructions. + """ from .barrier import Barrier - qubits = [] + qubits: List[QubitSpecifier] = [] if not qargs: # None qubits.extend(self.qubits) @@ -2342,7 +2497,12 @@ def barrier(self, *qargs): return self.append(Barrier(len(qubits)), qubits, []) - def delay(self, duration, qarg=None, unit="dt"): + def delay( + self, + duration: ParameterValueType, + qarg: Optional[QubitSpecifier] = None, + unit: str = "dt", + ) -> InstructionSet: """Apply :class:`~qiskit.circuit.Delay`. If qarg is None, applies to all qubits. When applying to multiple qubits, delays with the same duration will be created. @@ -2353,12 +2513,12 @@ def delay(self, duration, qarg=None, unit="dt"): Default is ``dt``, i.e. integer time unit depending on the target backend. Returns: - qiskit.Instruction: the attached delay instruction. + qiskit.circuit.InstructionSet: handle to the added instructions. Raises: CircuitError: if arguments have bad format. """ - qubits = [] + qubits: List[QubitSpecifier] = [] if qarg is None: # -> apply delays to all qubits for q in self.qubits: qubits.append(q) @@ -2381,59 +2541,161 @@ def delay(self, duration, qarg=None, unit="dt"): instructions.add(*inst) return instructions - def h(self, qubit): # pylint: disable=invalid-name - """Apply :class:`~qiskit.circuit.library.HGate`.""" + def h(self, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.HGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.h import HGate return self.append(HGate(), [qubit], []) def ch( self, - control_qubit, - target_qubit, # pylint: disable=invalid-name - label=None, - ctrl_state=None, - ): - """Apply :class:`~qiskit.circuit.library.CHGate`.""" + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.CHGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.h import CHGate return self.append( CHGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] ) - def i(self, qubit): - """Apply :class:`~qiskit.circuit.library.IGate`.""" + def i(self, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.IGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.i import IGate return self.append(IGate(), [qubit], []) - def id(self, qubit): # pylint: disable=invalid-name - """Apply :class:`~qiskit.circuit.library.IGate`.""" + def id(self, qubit: QubitSpecifier) -> InstructionSet: # pylint: disable=invalid-name + """Apply :class:`~qiskit.circuit.library.IGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + + See also: + QuantumCircuit.i: the same function. + """ return self.i(qubit) - def ms(self, theta, qubits): # pylint: disable=invalid-name - """Apply :class:`~qiskit.circuit.library.MSGate`.""" + def ms(self, theta: ParameterValueType, qubits: Sequence[QubitSpecifier]) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.generalized_gates.gms.MSGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The angle of the rotation. + qubits: The qubits to apply the gate to. + + Returns: + A handle to the instructions created. + """ # pylint: disable=cyclic-import from .library.generalized_gates.gms import MSGate return self.append(MSGate(len(qubits), theta), qubits) - def p(self, theta, qubit): - """Apply :class:`~qiskit.circuit.library.PhaseGate`.""" + def p(self, theta: ParameterValueType, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.PhaseGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: THe angle of the rotation. + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.p import PhaseGate return self.append(PhaseGate(theta), [qubit], []) - def cp(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CPhaseGate`.""" + def cp( + self, + theta: ParameterValueType, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.CPhaseGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The angle of the rotation. + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.p import CPhaseGate return self.append( CPhaseGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] ) - def mcp(self, lam, control_qubits, target_qubit): - """Apply :class:`~qiskit.circuit.library.MCPhaseGate`.""" + def mcp( + self, + lam: ParameterValueType, + control_qubits: Sequence[QubitSpecifier], + target_qubit: QubitSpecifier, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.MCPhaseGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + lam: The angle of the rotation. + control_qubits: The qubits used as the controls. + target_qubit: The qubit(s) targeted by the gate. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.p import MCPhaseGate num_ctrl_qubits = len(control_qubits) @@ -2441,130 +2703,419 @@ def mcp(self, lam, control_qubits, target_qubit): MCPhaseGate(lam, num_ctrl_qubits), control_qubits[:] + [target_qubit], [] ) - def r(self, theta, phi, qubit): - """Apply :class:`~qiskit.circuit.library.RGate`.""" + def r( + self, theta: ParameterValueType, phi: ParameterValueType, qubit: QubitSpecifier + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The angle of the rotation. + phi: The angle of the axis of rotation in the x-y plane. + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.r import RGate return self.append(RGate(theta, phi), [qubit], []) - def rv(self, vx, vy, vz, qubit): - """Apply :class:`~qiskit.circuit.library.RVGate`.""" + def rv( + self, + vx: ParameterValueType, + vy: ParameterValueType, + vz: ParameterValueType, + qubit: QubitSpecifier, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RVGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Rotation around an arbitrary rotation axis :math:`v`, where :math:`|v|` is the angle of + rotation in radians. + + Args: + vx: x-compenent of the rotation axis. + vy: y-compenent of the rotation axis. + vz: z-compenent of the rotation axis. + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.generalized_gates.rv import RVGate return self.append(RVGate(vx, vy, vz), [qubit], []) - def rccx(self, control_qubit1, control_qubit2, target_qubit): - """Apply :class:`~qiskit.circuit.library.RCCXGate`.""" + def rccx( + self, + control_qubit1: QubitSpecifier, + control_qubit2: QubitSpecifier, + target_qubit: QubitSpecifier, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RCCXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit1: The qubit(s) used as the first control. + control_qubit2: The qubit(s) used as the second control. + target_qubit: The qubit(s) targeted by the gate. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.x import RCCXGate return self.append(RCCXGate(), [control_qubit1, control_qubit2, target_qubit], []) - def rcccx(self, control_qubit1, control_qubit2, control_qubit3, target_qubit): - """Apply :class:`~qiskit.circuit.library.RC3XGate`.""" + def rcccx( + self, + control_qubit1: QubitSpecifier, + control_qubit2: QubitSpecifier, + control_qubit3: QubitSpecifier, + target_qubit: QubitSpecifier, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RC3XGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit1: The qubit(s) used as the first control. + control_qubit2: The qubit(s) used as the second control. + control_qubit3: The qubit(s) used as the third control. + target_qubit: The qubit(s) targeted by the gate. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.x import RC3XGate return self.append( RC3XGate(), [control_qubit1, control_qubit2, control_qubit3, target_qubit], [] ) - def rx(self, theta, qubit, label=None): # pylint: disable=invalid-name - """Apply :class:`~qiskit.circuit.library.RXGate`.""" + def rx( + self, theta: ParameterValueType, qubit: QubitSpecifier, label: Optional[str] = None + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The rotation angle of the gate. + qubit: The qubit(s) to apply the gate to. + label: The string label of the gate in the circuit. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.rx import RXGate return self.append(RXGate(theta, label=label), [qubit], []) - def crx(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CRXGate`.""" + def crx( + self, + theta: ParameterValueType, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.CRXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The angle of the rotation. + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.rx import CRXGate return self.append( CRXGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] ) - def rxx(self, theta, qubit1, qubit2): - """Apply :class:`~qiskit.circuit.library.RXXGate`.""" + def rxx( + self, theta: ParameterValueType, qubit1: QubitSpecifier, qubit2: QubitSpecifier + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RXXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The angle of the rotation. + qubit1: The qubit(s) to apply the gate to. + qubit2: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.rxx import RXXGate return self.append(RXXGate(theta), [qubit1, qubit2], []) - def ry(self, theta, qubit, label=None): # pylint: disable=invalid-name - """Apply :class:`~qiskit.circuit.library.RYGate`.""" + def ry( + self, theta: ParameterValueType, qubit: QubitSpecifier, label: Optional[str] = None + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RYGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The rotation angle of the gate. + qubit: The qubit(s) to apply the gate to. + label: The string label of the gate in the circuit. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.ry import RYGate return self.append(RYGate(theta, label=label), [qubit], []) - def cry(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CRYGate`.""" + def cry( + self, + theta: ParameterValueType, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.CRYGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The angle of the rotation. + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.ry import CRYGate return self.append( CRYGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] ) - def ryy(self, theta, qubit1, qubit2): - """Apply :class:`~qiskit.circuit.library.RYYGate`.""" + def ryy( + self, theta: ParameterValueType, qubit1: QubitSpecifier, qubit2: QubitSpecifier + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RYYGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The rotation angle of the gate. + qubit1: The qubit(s) to apply the gate to. + qubit2: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.ryy import RYYGate return self.append(RYYGate(theta), [qubit1, qubit2], []) - def rz(self, phi, qubit): # pylint: disable=invalid-name - """Apply :class:`~qiskit.circuit.library.RZGate`.""" + def rz(self, phi: ParameterValueType, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RYGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + phi: The rotation angle of the gate. + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.rz import RZGate return self.append(RZGate(phi), [qubit], []) - def crz(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CRZGate`.""" + def crz( + self, + theta: ParameterValueType, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.CRZGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The angle of the rotation. + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.rz import CRZGate return self.append( CRZGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] ) - def rzx(self, theta, qubit1, qubit2): - """Apply :class:`~qiskit.circuit.library.RZXGate`.""" + def rzx( + self, theta: ParameterValueType, qubit1: QubitSpecifier, qubit2: QubitSpecifier + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RZXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The rotation angle of the gate. + qubit1: The qubit(s) to apply the gate to. + qubit2: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.rzx import RZXGate return self.append(RZXGate(theta), [qubit1, qubit2], []) - def rzz(self, theta, qubit1, qubit2): - """Apply :class:`~qiskit.circuit.library.RZZGate`.""" + def rzz( + self, theta: ParameterValueType, qubit1: QubitSpecifier, qubit2: QubitSpecifier + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.RZZGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The rotation angle of the gate. + qubit1: The qubit(s) to apply the gate to. + qubit2: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.rzz import RZZGate return self.append(RZZGate(theta), [qubit1, qubit2], []) - def ecr(self, qubit1, qubit2): - """Apply :class:`~qiskit.circuit.library.ECRGate`.""" + def ecr(self, qubit1: QubitSpecifier, qubit2: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.ECRGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit1, qubit2: The qubits to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.ecr import ECRGate return self.append(ECRGate(), [qubit1, qubit2], []) - def s(self, qubit): # pylint: disable=invalid-name - """Apply :class:`~qiskit.circuit.library.SGate`.""" + def s(self, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.SGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.s import SGate return self.append(SGate(), [qubit], []) - def sdg(self, qubit): - """Apply :class:`~qiskit.circuit.library.SdgGate`.""" + def sdg(self, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.SdgGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.s import SdgGate return self.append(SdgGate(), [qubit], []) - def swap(self, qubit1, qubit2): - """Apply :class:`~qiskit.circuit.library.SwapGate`.""" + def swap(self, qubit1: QubitSpecifier, qubit2: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.SwapGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit1, qubit2: The qubits to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.swap import SwapGate return self.append(SwapGate(), [qubit1, qubit2], []) - def iswap(self, qubit1, qubit2): - """Apply :class:`~qiskit.circuit.library.iSwapGate`.""" + def iswap(self, qubit1: QubitSpecifier, qubit2: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.iSwapGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit1, qubit2: The qubits to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.iswap import iSwapGate return self.append(iSwapGate(), [qubit1, qubit2], []) - def cswap(self, control_qubit, target_qubit1, target_qubit2, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CSwapGate`.""" + def cswap( + self, + control_qubit: QubitSpecifier, + target_qubit1: QubitSpecifier, + target_qubit2: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.CSwapGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit: The qubit(s) used as the control. + target_qubit1: The qubit(s) targeted by the gate. + target_qubit2: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.swap import CSwapGate return self.append( @@ -2573,24 +3124,81 @@ def cswap(self, control_qubit, target_qubit1, target_qubit2, label=None, ctrl_st [], ) - def fredkin(self, control_qubit, target_qubit1, target_qubit2): - """Apply :class:`~qiskit.circuit.library.CSwapGate`.""" + def fredkin( + self, + control_qubit: QubitSpecifier, + target_qubit1: QubitSpecifier, + target_qubit2: QubitSpecifier, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.CSwapGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit: The qubit(s) used as the control. + target_qubit1: The qubit(s) targeted by the gate. + target_qubit2: The qubit(s) targeted by the gate. + + Returns: + A handle to the instructions created. + + See Also: + QuantumCircuit.cswap: the same function with a different name. + """ return self.cswap(control_qubit, target_qubit1, target_qubit2) - def sx(self, qubit): # pylint: disable=invalid-name - """Apply :class:`~qiskit.circuit.library.SXGate`.""" + def sx(self, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.SXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.sx import SXGate return self.append(SXGate(), [qubit], []) - def sxdg(self, qubit): - """Apply :class:`~qiskit.circuit.library.SXdgGate`.""" + def sxdg(self, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.SXdgGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.sx import SXdgGate return self.append(SXdgGate(), [qubit], []) - def csx(self, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CSXGate`.""" + def csx( + self, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.CSXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.sx import CSXGate return self.append( @@ -2599,26 +3207,90 @@ def csx(self, control_qubit, target_qubit, label=None, ctrl_state=None): [], ) - def t(self, qubit): # pylint: disable=invalid-name - """Apply :class:`~qiskit.circuit.library.TGate`.""" + def t(self, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.TGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.t import TGate return self.append(TGate(), [qubit], []) - def tdg(self, qubit): - """Apply :class:`~qiskit.circuit.library.TdgGate`.""" + def tdg(self, qubit: QubitSpecifier) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.TdgGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.t import TdgGate return self.append(TdgGate(), [qubit], []) - def u(self, theta, phi, lam, qubit): - """Apply :class:`~qiskit.circuit.library.UGate`.""" + def u( + self, + theta: ParameterValueType, + phi: ParameterValueType, + lam: ParameterValueType, + qubit: QubitSpecifier, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.UGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The :math:`\theta` rotation angle of the gate. + phi: The :math:`\phi` rotation angle of the gate. + lam: The :math:`\lambda` rotation angle of the gate. + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.u import UGate return self.append(UGate(theta, phi, lam), [qubit], []) - def cu(self, theta, phi, lam, gamma, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CUGate`.""" + def cu( + self, + theta: ParameterValueType, + phi: ParameterValueType, + lam: ParameterValueType, + gamma: ParameterValueType, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.CUGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The :math:`\theta` rotation angle of the gate. + phi: The :math:`\phi` rotation angle of the gate. + lam: The :math:`\lambda` rotation angle of the gate. + gamma: The global phase applied of the U gate, if applied. + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.u import CUGate return self.append( @@ -2634,8 +3306,18 @@ def cu(self, theta, phi, lam, gamma, control_qubit, target_qubit, label=None, ct "QuantumCircuit.p method instead, which acts " "identically." ) - def u1(self, theta, qubit): - """Apply :class:`~qiskit.circuit.library.U1Gate`.""" + def u1(self, theta: ParameterValueType, qubit: QubitSpecifier) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.U1Gate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The :math:`\theta` rotation angle of the gate. + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.u1 import U1Gate return self.append(U1Gate(theta), [qubit], []) @@ -2647,8 +3329,30 @@ def u1(self, theta, qubit): "QuantumCircuit.cp method instead, which acts " "identically." ) - def cu1(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CU1Gate`.""" + def cu1( + self, + theta: ParameterValueType, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.CU1Gate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The :math:`\theta` rotation angle of the gate. + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.u1 import CU1Gate return self.append( @@ -2662,8 +3366,24 @@ def cu1(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): "QuantumCircuit.mcp method instead, which acts " "identically." ) - def mcu1(self, lam, control_qubits, target_qubit): - """Apply :class:`~qiskit.circuit.library.MCU1Gate`.""" + def mcu1( + self, + lam: ParameterValueType, + control_qubits: Sequence[QubitSpecifier], + target_qubit: QubitSpecifier, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.MCU1Gate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + lam: The :math:`\lambda` rotation angle of the gate. + control_qubits: The qubits used as the controls. + target_qubit: The qubit(s) targeted by the gate. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.u1 import MCU1Gate num_ctrl_qubits = len(control_qubits) @@ -2678,8 +3398,21 @@ def mcu1(self, lam, control_qubits, target_qubit): "terms of QuantumCircuit.p and QuantumCircuit.sx: " "u2(φ,λ) = p(π/2+φ) sx p(λ-π/2) (1 pulse on hardware)." ) - def u2(self, phi, lam, qubit): - """Apply :class:`~qiskit.circuit.library.U2Gate`.""" + def u2( + self, phi: ParameterValueType, lam: ParameterValueType, qubit: QubitSpecifier + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.U2Gate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + phi: The :math:`\phi` rotation angle of the gate. + lam: The :math:`\lambda` rotation angle of the gate. + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.u2 import U2Gate return self.append(U2Gate(phi, lam), [qubit], []) @@ -2691,8 +3424,26 @@ def u2(self, phi, lam, qubit): "decompose u3 in terms of QuantumCircuit.p and QuantumCircuit.sx: " "u3(ϴ,φ,λ) = p(φ+π) sx p(ϴ+π) sx p(λ) (2 pulses on hardware)." ) - def u3(self, theta, phi, lam, qubit): - """Apply :class:`~qiskit.circuit.library.U3Gate`.""" + def u3( + self, + theta: ParameterValueType, + phi: ParameterValueType, + lam: ParameterValueType, + qubit: QubitSpecifier, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.U3Gate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The :math:`\theta` rotation angle of the gate. + phi: The :math:`\phi` rotation angle of the gate. + lam: The :math:`\lambda` rotation angle of the gate. + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.u3 import U3Gate return self.append(U3Gate(theta, phi, lam), [qubit], []) @@ -2703,8 +3454,34 @@ def u3(self, theta, phi, lam, qubit): "use the QuantumCircuit.cu method instead, where " "cu3(ϴ,φ,λ) = cu(ϴ,φ,λ,0)." ) - def cu3(self, theta, phi, lam, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CU3Gate`.""" + def cu3( + self, + theta: ParameterValueType, + phi: ParameterValueType, + lam: ParameterValueType, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.CU3Gate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + theta: The :math:`\theta` rotation angle of the gate. + phi: The :math:`\phi` rotation angle of the gate. + lam: The :math:`\lambda` rotation angle of the gate. + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.u3 import CU3Gate return self.append( @@ -2713,44 +3490,116 @@ def cu3(self, theta, phi, lam, control_qubit, target_qubit, label=None, ctrl_sta [], ) - def x(self, qubit, label=None): - """Apply :class:`~qiskit.circuit.library.XGate`.""" + def x(self, qubit: QubitSpecifier, label: Optional[str] = None) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.XGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + label: The string label of the gate in the circuit. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.x import XGate return self.append(XGate(label=label), [qubit], []) def cx( self, - control_qubit, - target_qubit, # pylint: disable=invalid-name - label=None, - ctrl_state=None, - ): - """Apply :class:`~qiskit.circuit.library.CXGate`.""" + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.CXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ + from .library.standard_gates.x import CXGate return self.append( CXGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] ) - def cnot(self, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CXGate`.""" - self.cx(control_qubit, target_qubit, label, ctrl_state) + def cnot( + self, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.CXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit: The qubit(s) used as the control. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + + See Also: + QuantumCircuit.cx: the same function with a different name. + """ + return self.cx(control_qubit, target_qubit, label, ctrl_state) + + def dcx(self, qubit1: QubitSpecifier, qubit2: QubitSpecifier) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.DCXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. - def dcx(self, qubit1, qubit2): - """Apply :class:`~qiskit.circuit.library.DCXGate`.""" + Args: + qubit1: The qubit(s) to apply the gate to. + qubit2: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.dcx import DCXGate return self.append(DCXGate(), [qubit1, qubit2], []) def ccx( self, - control_qubit1, - control_qubit2, - target_qubit, - ctrl_state=None, - ): - """Apply :class:`~qiskit.circuit.library.CCXGate`.""" + control_qubit1: QubitSpecifier, + control_qubit2: QubitSpecifier, + target_qubit: QubitSpecifier, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.CCXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit1: The qubit(s) used as the first control. + control_qubit2: The qubit(s) used as the second control. + target_qubit: The qubit(s) targeted by the gate. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.x import CCXGate return self.append( @@ -2759,19 +3608,60 @@ def ccx( [], ) - def toffoli(self, control_qubit1, control_qubit2, target_qubit): - """Apply :class:`~qiskit.circuit.library.CCXGate`.""" - self.ccx(control_qubit1, control_qubit2, target_qubit) + def toffoli( + self, + control_qubit1: QubitSpecifier, + control_qubit2: QubitSpecifier, + target_qubit: QubitSpecifier, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.CCXGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit1: The qubit(s) used as the first control. + control_qubit2: The qubit(s) used as the second control. + target_qubit: The qubit(s) targeted by the gate. + + Returns: + A handle to the instructions created. + + See Also: + QuantumCircuit.ccx: the same gate with a different name. + """ + return self.ccx(control_qubit1, control_qubit2, target_qubit) - def mcx(self, control_qubits, target_qubit, ancilla_qubits=None, mode="noancilla"): + def mcx( + self, + control_qubits: Sequence[QubitSpecifier], + target_qubit: QubitSpecifier, + ancilla_qubits: Optional[Union[QubitSpecifier, Sequence[QubitSpecifier]]] = None, + mode: str = "noancilla", + ) -> InstructionSet: """Apply :class:`~qiskit.circuit.library.MCXGate`. The multi-cX gate can be implemented using different techniques, which use different numbers of ancilla qubits and have varying circuit depth. These modes are: + - 'noancilla': Requires 0 ancilla qubits. - 'recursion': Requires 1 ancilla qubit if more than 4 controls are used, otherwise 0. - 'v-chain': Requires 2 less ancillas than the number of control qubits. - 'v-chain-dirty': Same as for the clean ancillas (but the circuit will be longer). + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubits: The qubits used as the controls. + target_qubit: The qubit(s) targeted by the gate. + ancilla_qubits: The qubits used as the ancillae, if the mode requires them. + mode: The choice of mode, explained further above. + + Returns: + A handle to the instructions created. + + Raises: + ValueError: if the given mode is not known, or if too few ancilla qubits are passed. + AttributeError: if no ancilla qubits are passed, but some are needed. """ from .library.standard_gates.x import MCXGrayCode, MCXRecursive, MCXVChain @@ -2819,51 +3709,158 @@ def mcx(self, control_qubits, target_qubit, ancilla_qubits=None, mode="noancilla return self.append(gate, control_qubits[:] + [target_qubit] + ancilla_qubits[:], []) - def mct(self, control_qubits, target_qubit, ancilla_qubits=None, mode="noancilla"): - """Apply :class:`~qiskit.circuit.library.MCXGate`.""" + def mct( + self, + control_qubits: Sequence[QubitSpecifier], + target_qubit: QubitSpecifier, + ancilla_qubits: Optional[Union[QubitSpecifier, Sequence[QubitSpecifier]]] = None, + mode: str = "noancilla", + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.MCXGate`. + + The multi-cX gate can be implemented using different techniques, which use different numbers + of ancilla qubits and have varying circuit depth. These modes are: + + - 'noancilla': Requires 0 ancilla qubits. + - 'recursion': Requires 1 ancilla qubit if more than 4 controls are used, otherwise 0. + - 'v-chain': Requires 2 less ancillas than the number of control qubits. + - 'v-chain-dirty': Same as for the clean ancillas (but the circuit will be longer). + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubits: The qubits used as the controls. + target_qubit: The qubit(s) targeted by the gate. + ancilla_qubits: The qubits used as the ancillae, if the mode requires them. + mode: The choice of mode, explained further above. + + Returns: + A handle to the instructions created. + + Raises: + ValueError: if the given mode is not known, or if too few ancilla qubits are passed. + AttributeError: if no ancilla qubits are passed, but some are needed. + + See Also: + QuantumCircuit.mcx: the same gate with a different name. + """ return self.mcx(control_qubits, target_qubit, ancilla_qubits, mode) - def y(self, qubit): - """Apply :class:`~qiskit.circuit.library.YGate`.""" + def y(self, qubit: QubitSpecifier) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.YGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.y import YGate return self.append(YGate(), [qubit], []) def cy( self, - control_qubit, - target_qubit, # pylint: disable=invalid-name - label=None, - ctrl_state=None, - ): - """Apply :class:`~qiskit.circuit.library.CYGate`.""" + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.CYGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit: The qubit(s) used as the controls. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.y import CYGate return self.append( CYGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] ) - def z(self, qubit): - """Apply :class:`~qiskit.circuit.library.ZGate`.""" + def z(self, qubit: QubitSpecifier) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.ZGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + qubit: The qubit(s) to apply the gate to. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.z import ZGate return self.append(ZGate(), [qubit], []) - def cz(self, control_qubit, target_qubit, label=None, ctrl_state=None): - """Apply :class:`~qiskit.circuit.library.CZGate`.""" + def cz( + self, + control_qubit: QubitSpecifier, + target_qubit: QubitSpecifier, + label: Optional[str] = None, + ctrl_state: Optional[Union[str, int]] = None, + ) -> InstructionSet: + r"""Apply :class:`~qiskit.circuit.library.CZGate`. + + For the full matrix form of this gate, see the underlying gate documentation. + + Args: + control_qubit: The qubit(s) used as the controls. + target_qubit: The qubit(s) targeted by the gate. + label: The string label of the gate in the circuit. + ctrl_state: + The control state in decimal, or as a bitstring (e.g. '1'). Defaults to controlling + on the '1' state. + + Returns: + A handle to the instructions created. + """ from .library.standard_gates.z import CZGate return self.append( CZGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] ) - def pauli(self, pauli_string, qubits): - """Apply :class:`~qiskit.circuit.library.PauliGate`.""" + def pauli( + self, + pauli_string: str, + qubits: Sequence[QubitSpecifier], + ) -> InstructionSet: + """Apply :class:`~qiskit.circuit.library.PauliGate`. + + Args: + pauli_string: A string representing the Pauli operator to apply, e.g. 'XX'. + qubits: The qubits to apply this gate to. + + Returns: + A handle to the instructions created. + """ from qiskit.circuit.library.generalized_gates.pauli import PauliGate return self.append(PauliGate(pauli_string), qubits, []) - def add_calibration(self, gate, qubits, schedule, params=None): + def add_calibration( + self, + gate: Union[Gate, str], + qubits: Sequence[int], + # Schedule has the type `qiskit.pulse.Schedule`, but `qiskit.pulse` cannot be imported + # while this module is, and so Sphinx will not accept a forward reference to it. Sphinx + # needs the types available at runtime, whereas mypy will accept it, because it handles the + # type checking by static analysis. + schedule, + params: Optional[Sequence[ParameterValueType]] = None, + ) -> None: """Register a low-level, custom pulse definition for the given gate. Args: @@ -2978,7 +3975,7 @@ def qubit_stop_time(self, *qubits: Union[Qubit, int]) -> float: return 0 # If there are no instructions over bits -def _circuit_from_qasm(qasm): +def _circuit_from_qasm(qasm: Qasm) -> "QuantumCircuit": # pylint: disable=cyclic-import from qiskit.converters import ast_to_dag from qiskit.converters import dag_to_circuit @@ -2996,7 +3993,7 @@ def _standard_compare(value1, value2): return 0 -def _compare_parameters(param1, param2): +def _compare_parameters(param1: Parameter, param2: Parameter) -> int: if isinstance(param1, ParameterVectorElement) and isinstance(param2, ParameterVectorElement): # if they belong to a vector with the same name, sort by index if param1.vector.name == param2.vector.name: @@ -3007,8 +4004,10 @@ def _compare_parameters(param1, param2): def _add_sub_instruction_to_existing_composite_circuits( - instruction, existing_gate_names, existing_composite_circuits -): + instruction: Instruction, + existing_gate_names: List[str], + existing_composite_circuits: List[Instruction], +) -> None: """Recursively add undefined sub-instructions in the definition of the given instruction to existing_composite_circuit list. """ @@ -3023,7 +4022,7 @@ def _add_sub_instruction_to_existing_composite_circuits( ) -def _get_composite_circuit_qasm_from_instruction(instruction): +def _get_composite_circuit_qasm_from_instruction(instruction: Instruction) -> str: """Returns OpenQASM string composite circuit given an instruction. The given instruction should be the result of composite_circuit.to_instruction().""" @@ -3064,7 +4063,9 @@ def _get_composite_circuit_qasm_from_instruction(instruction): return qasm_string -def _insert_composite_gate_definition_qasm(string_temp, existing_composite_circuits, extension_lib): +def _insert_composite_gate_definition_qasm( + string_temp: str, existing_composite_circuits: List[Instruction], extension_lib: str +) -> str: """Insert composite gate definition QASM code right after extension library in the header""" gate_definition_string = "" diff --git a/qiskit/circuit/reset.py b/qiskit/circuit/reset.py index ddf7e37da8f0..eb5921602846 100644 --- a/qiskit/circuit/reset.py +++ b/qiskit/circuit/reset.py @@ -13,7 +13,6 @@ """ Qubit reset to computational zero. """ -from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.instruction import Instruction @@ -27,11 +26,3 @@ def __init__(self): def broadcast_arguments(self, qargs, cargs): for qarg in qargs[0]: yield [qarg], [] - - -def reset(self, qubit): - """Reset q.""" - return self.append(Reset(), [qubit], []) - - -QuantumCircuit.reset = reset diff --git a/releasenotes/notes/gate-type-hints-b287215190144ceb.yaml b/releasenotes/notes/gate-type-hints-b287215190144ceb.yaml new file mode 100644 index 000000000000..c91a8bdb9e85 --- /dev/null +++ b/releasenotes/notes/gate-type-hints-b287215190144ceb.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + Type hints are now included for all standard gates, and the gate methods in + :obj:`.QuantumCircuit`.