diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 20af254e76cb..13003c7e83b3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,7 +30,7 @@ pulse/ @Qiskit/terra-core @eggerdj @wshanks synthesis/ @Qiskit/terra-core @alexanderivrii @ShellyGarion scheduler/ @Qiskit/terra-core @eggerdj @wshanks visualization/ @Qiskit/terra-core @nonhermitian -primitives/ @Qiskit/terra-core @ikkoham @t-imamichi +primitives/ @Qiskit/terra-core @Qiskit/qiskit-primitives # Override the release notes directories to have _no_ code owners, so any review # from somebody with write access is acceptable. /releasenotes/notes diff --git a/crates/accelerate/src/quantum_circuit/circuit_data.rs b/crates/accelerate/src/quantum_circuit/circuit_data.rs index 76c3c78d57ed..033bd2ea7005 100644 --- a/crates/accelerate/src/quantum_circuit/circuit_data.rs +++ b/crates/accelerate/src/quantum_circuit/circuit_data.rs @@ -213,32 +213,33 @@ impl CircuitData { Ok((ty, args, None::<()>, self_.iter()?).into_py(py)) } - /// Returns the current sequence of registered :class:`.Qubit` - /// instances as a list. + /// Returns the current sequence of registered :class:`.Qubit` instances as a list. /// - /// .. note:: + /// .. warning:: /// - /// This list is not kept in sync with the container. + /// Do not modify this list yourself. It will invalidate the :class:`CircuitData` data + /// structures. /// /// Returns: /// list(:class:`.Qubit`): The current sequence of registered qubits. #[getter] - pub fn qubits(&self, py: Python<'_>) -> PyObject { - PyList::new(py, self.qubits.as_ref(py)).into_py(py) + pub fn qubits(&self, py: Python<'_>) -> Py { + self.qubits.clone_ref(py) } /// Returns the current sequence of registered :class:`.Clbit` /// instances as a list. /// - /// .. note:: + /// .. warning:: /// - /// This list is not kept in sync with the container. + /// Do not modify this list yourself. It will invalidate the :class:`CircuitData` data + /// structures. /// /// Returns: /// list(:class:`.Clbit`): The current sequence of registered clbits. #[getter] - pub fn clbits(&self, py: Python<'_>) -> PyObject { - PyList::new(py, self.clbits.as_ref(py)).into_py(py) + pub fn clbits(&self, py: Python<'_>) -> Py { + self.clbits.clone_ref(py) } /// Registers a :class:`.Qubit` instance. @@ -251,7 +252,13 @@ impl CircuitData { /// ValueError: The specified ``bit`` is already present and flag ``strict`` /// was provided. #[pyo3(signature = (bit, *, strict=true))] - pub fn add_qubit(&mut self, py: Python<'_>, bit: &PyAny, strict: bool) -> PyResult<()> { + pub fn add_qubit(&mut self, py: Python, bit: &PyAny, strict: bool) -> PyResult<()> { + if self.qubits_native.len() != self.qubits.as_ref(bit.py()).len() { + return Err(PyRuntimeError::new_err(concat!( + "This circuit's 'qubits' list has become out of sync with the circuit data.", + " Did something modify it?" + ))); + } let idx: BitType = self.qubits_native.len().try_into().map_err(|_| { PyRuntimeError::new_err( "The number of qubits in the circuit has exceeded the maximum capacity", @@ -263,7 +270,7 @@ impl CircuitData { .is_ok() { self.qubits_native.push(bit.into_py(py)); - self.qubits = PyList::new(py, &self.qubits_native).into_py(py); + self.qubits.as_ref(py).append(bit)?; } else if strict { return Err(PyValueError::new_err(format!( "Existing bit {:?} cannot be re-added in strict mode.", @@ -283,7 +290,13 @@ impl CircuitData { /// ValueError: The specified ``bit`` is already present and flag ``strict`` /// was provided. #[pyo3(signature = (bit, *, strict=true))] - pub fn add_clbit(&mut self, py: Python<'_>, bit: &PyAny, strict: bool) -> PyResult<()> { + pub fn add_clbit(&mut self, py: Python, bit: &PyAny, strict: bool) -> PyResult<()> { + if self.clbits_native.len() != self.clbits.as_ref(bit.py()).len() { + return Err(PyRuntimeError::new_err(concat!( + "This circuit's 'clbits' list has become out of sync with the circuit data.", + " Did something modify it?" + ))); + } let idx: BitType = self.clbits_native.len().try_into().map_err(|_| { PyRuntimeError::new_err( "The number of clbits in the circuit has exceeded the maximum capacity", @@ -295,7 +308,7 @@ impl CircuitData { .is_ok() { self.clbits_native.push(bit.into_py(py)); - self.clbits = PyList::new(py, &self.clbits_native).into_py(py); + self.clbits.as_ref(py).append(bit)?; } else if strict { return Err(PyValueError::new_err(format!( "Existing bit {:?} cannot be re-added in strict mode.", diff --git a/docs/apidoc/index.rst b/docs/apidoc/index.rst index 9d1eb2f64960..3fc3e25e457d 100644 --- a/docs/apidoc/index.rst +++ b/docs/apidoc/index.rst @@ -35,7 +35,6 @@ API Reference quantum_info result tools - tools_jupyter transpiler transpiler_passes transpiler_preset diff --git a/docs/apidoc/tools_jupyter.rst b/docs/apidoc/tools_jupyter.rst deleted file mode 100644 index 92691f6ba9a8..000000000000 --- a/docs/apidoc/tools_jupyter.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. _qiskit-tools-jupyter: - -.. automodule:: qiskit.tools.jupyter - :no-members: - :no-inherited-members: - :no-special-members: diff --git a/pyproject.toml b/pyproject.toml index 04bafbf3f995..d42b249a1058 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,6 @@ qasm3-import = [ ] visualization = [ "matplotlib >= 3.3", - "ipywidgets >= 7.3.0", "pydot", "Pillow >= 4.2.1", "pylatexenc >= 1.4", diff --git a/qiskit/__init__.py b/qiskit/__init__.py index ceaddc907938..8fc93134d647 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -74,8 +74,6 @@ _config = _user_config.get_config() -# Moved to after IBMQ and Aer imports due to import issues -# with other modules that check for IBMQ (tools) from qiskit.execute_function import execute from qiskit.compiler import transpile, assemble, schedule, sequence @@ -125,64 +123,13 @@ def __getattr__(self, attr): return getattr(self.aer, attr) -class IBMQWrapper: - """Lazy loading wrapper for IBMQ provider.""" - - def __init__(self): - self.ibmq = None - - def __bool__(self): - if self.ibmq is None: - try: - from qiskit.providers import ibmq - - self.ibmq = ibmq.IBMQ - warnings.warn( - "The qiskit.IBMQ entrypoint and the qiskit-ibmq-provider package (" - "accessible from 'qiskit.providers.ibmq`) are deprecated and will be removed " - "in a future release. Instead you should use the qiskit-ibm-provider package " - "which is accessible from 'qiskit_ibm_provider'. You can install it with " - "'pip install qiskit_ibm_provider'", - DeprecationWarning, - stacklevel=2, - ) - - except ImportError: - return False - return True - - def __getattr__(self, attr): - if not self.ibmq: - try: - from qiskit.providers import ibmq - - self.ibmq = ibmq.IBMQ - warnings.warn( - "The qiskit.IBMQ entrypoint and the qiskit-ibmq-provider package (" - "accessible from 'qiskit.providers.ibmq`) are deprecated and will be removed " - "in a future release. Instead you should use the qiskit-ibm-provider package " - "which is accessible from 'qiskit_ibm_provider'. You can install it with " - "'pip install qiskit_ibm_provider'. Just replace 'qiskit.IBMQ' with " - "'qiskit_ibm_provider.IBMProvider'", - DeprecationWarning, - stacklevel=2, - ) - except ImportError as ex: - raise MissingOptionalLibraryError( - "qiskit-ibmq-provider", "IBMQ provider", "pip install qiskit-ibmq-provider" - ) from ex - return getattr(self.ibmq, attr) - - Aer = AerWrapper() -IBMQ = IBMQWrapper() __all__ = [ "Aer", "AncillaRegister", "BasicProvider", "ClassicalRegister", - "IBMQ", "MissingOptionalLibraryError", "QiskitError", "QuantumCircuit", diff --git a/qiskit/circuit/delay.py b/qiskit/circuit/delay.py index 97bc81099e75..68d76dc03efb 100644 --- a/qiskit/circuit/delay.py +++ b/qiskit/circuit/delay.py @@ -16,6 +16,7 @@ import numpy as np from qiskit.circuit.exceptions import CircuitError from qiskit.circuit.instruction import Instruction +from qiskit.circuit.gate import Gate from qiskit.circuit.parameterexpression import ParameterExpression @@ -29,13 +30,12 @@ def __init__(self, duration, unit="dt"): super().__init__("delay", 1, 0, params=[duration], unit=unit) + broadcast_arguments = Gate.broadcast_arguments + def inverse(self): """Special case. Return self.""" return self - def broadcast_arguments(self, qargs, cargs): - yield [qarg for sublist in qargs for qarg in sublist], [] - def c_if(self, classical, val): raise CircuitError("Conditional Delay is not yet implemented.") diff --git a/qiskit/circuit/library/basis_change/qft.py b/qiskit/circuit/library/basis_change/qft.py index 7e9262d81943..9dcb1251453f 100644 --- a/qiskit/circuit/library/basis_change/qft.py +++ b/qiskit/circuit/library/basis_change/qft.py @@ -41,7 +41,7 @@ class QFT(BlueprintCircuit): .. plot:: from qiskit.circuit.library import QFT - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = QFT(4) _generate_circuit_library_visualization(circuit) @@ -51,7 +51,7 @@ class QFT(BlueprintCircuit): .. plot:: from qiskit.circuit.library import QFT - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = QFT(4).inverse() _generate_circuit_library_visualization(circuit) @@ -67,7 +67,7 @@ class QFT(BlueprintCircuit): .. plot:: from qiskit.circuit.library import QFT - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = QFT(5, approximation_degree=2) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/boolean_logic/inner_product.py b/qiskit/circuit/library/boolean_logic/inner_product.py index 08524f51048a..6efbc8a02913 100644 --- a/qiskit/circuit/library/boolean_logic/inner_product.py +++ b/qiskit/circuit/library/boolean_logic/inner_product.py @@ -56,7 +56,7 @@ class InnerProduct(QuantumCircuit): .. plot:: from qiskit.circuit.library import InnerProduct - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = InnerProduct(4) _generate_circuit_library_visualization(circuit) """ diff --git a/qiskit/circuit/library/boolean_logic/quantum_and.py b/qiskit/circuit/library/boolean_logic/quantum_and.py index 97c2a0e143b9..ce2d253ca85a 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_and.py +++ b/qiskit/circuit/library/boolean_logic/quantum_and.py @@ -32,7 +32,7 @@ class AND(QuantumCircuit): .. plot:: from qiskit.circuit.library import AND - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = AND(5) _generate_circuit_library_visualization(circuit) @@ -43,7 +43,7 @@ class AND(QuantumCircuit): .. plot:: from qiskit.circuit.library import AND - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = AND(5, flags=[-1, 0, 0, 1, 1]) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/boolean_logic/quantum_or.py b/qiskit/circuit/library/boolean_logic/quantum_or.py index 2ac114ffb1b1..91d6c4fbdd52 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_or.py +++ b/qiskit/circuit/library/boolean_logic/quantum_or.py @@ -33,7 +33,7 @@ class OR(QuantumCircuit): .. plot:: from qiskit.circuit.library import OR - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = OR(5) _generate_circuit_library_visualization(circuit) @@ -44,7 +44,7 @@ class OR(QuantumCircuit): .. plot:: from qiskit.circuit.library import OR - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = OR(5, flags=[-1, 0, 0, 1, 1]) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/boolean_logic/quantum_xor.py b/qiskit/circuit/library/boolean_logic/quantum_xor.py index 08b0055248fa..5b230345137d 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_xor.py +++ b/qiskit/circuit/library/boolean_logic/quantum_xor.py @@ -48,7 +48,7 @@ def __init__( .. plot:: from qiskit.circuit.library import XOR - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = XOR(5, seed=42) _generate_circuit_library_visualization(circuit) """ diff --git a/qiskit/circuit/library/fourier_checking.py b/qiskit/circuit/library/fourier_checking.py index b31dc9748e83..9035fbc360d4 100644 --- a/qiskit/circuit/library/fourier_checking.py +++ b/qiskit/circuit/library/fourier_checking.py @@ -66,7 +66,7 @@ def __init__(self, f: List[int], g: List[int]) -> None: .. plot:: from qiskit.circuit.library import FourierChecking - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization f = [1, -1, -1, -1] g = [1, 1, -1, -1] circuit = FourierChecking(f, g) diff --git a/qiskit/circuit/library/generalized_gates/gms.py b/qiskit/circuit/library/generalized_gates/gms.py index 46b28cac60af..fccb332d438c 100644 --- a/qiskit/circuit/library/generalized_gates/gms.py +++ b/qiskit/circuit/library/generalized_gates/gms.py @@ -44,7 +44,7 @@ class GMS(QuantumCircuit): .. plot:: from qiskit.circuit.library import GMS - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GMS(num_qubits=3, theta=[[0, np.pi/4, np.pi/8], [0, 0, np.pi/2], diff --git a/qiskit/circuit/library/generalized_gates/gr.py b/qiskit/circuit/library/generalized_gates/gr.py index acb83909cce0..e79851db4779 100644 --- a/qiskit/circuit/library/generalized_gates/gr.py +++ b/qiskit/circuit/library/generalized_gates/gr.py @@ -47,7 +47,7 @@ class GR(QuantumCircuit): .. plot:: from qiskit.circuit.library import GR - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GR(num_qubits=3, theta=np.pi/4, phi=np.pi/2) _generate_circuit_library_visualization(circuit) @@ -101,7 +101,7 @@ class GRX(GR): .. plot:: from qiskit.circuit.library import GRX - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GRX(num_qubits=3, theta=np.pi/4) _generate_circuit_library_visualization(circuit) @@ -149,7 +149,7 @@ class GRY(GR): .. plot:: from qiskit.circuit.library import GRY - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GRY(num_qubits=3, theta=np.pi/4) _generate_circuit_library_visualization(circuit) @@ -197,7 +197,7 @@ class GRZ(QuantumCircuit): .. plot:: from qiskit.circuit.library import GRZ - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import numpy as np circuit = GRZ(num_qubits=3, phi=np.pi/2) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/generalized_gates/mcmt.py b/qiskit/circuit/library/generalized_gates/mcmt.py index 1a529b8859e1..c6ec0bda116e 100644 --- a/qiskit/circuit/library/generalized_gates/mcmt.py +++ b/qiskit/circuit/library/generalized_gates/mcmt.py @@ -164,7 +164,7 @@ class MCMTVChain(MCMT): .. plot:: from qiskit.circuit.library import MCMTVChain, ZGate - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = MCMTVChain(ZGate(), 2, 2) _generate_circuit_library_visualization(circuit.decompose()) diff --git a/qiskit/circuit/library/generalized_gates/permutation.py b/qiskit/circuit/library/generalized_gates/permutation.py index 44576518d624..d9151c09f7f7 100644 --- a/qiskit/circuit/library/generalized_gates/permutation.py +++ b/qiskit/circuit/library/generalized_gates/permutation.py @@ -62,7 +62,7 @@ def __init__( .. plot:: from qiskit.circuit.library import Permutation - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization A = [2,4,3,0,1] circuit = Permutation(5, A) _generate_circuit_library_visualization(circuit.decompose()) @@ -130,7 +130,7 @@ def __init__( from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.library import PermutationGate - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization A = [2,4,3,0,1] permutation = PermutationGate(A) circuit = QuantumCircuit(5) diff --git a/qiskit/circuit/library/graph_state.py b/qiskit/circuit/library/graph_state.py index 25a3ccc95a2c..ceefff7971db 100644 --- a/qiskit/circuit/library/graph_state.py +++ b/qiskit/circuit/library/graph_state.py @@ -42,7 +42,7 @@ class GraphState(QuantumCircuit): .. plot:: from qiskit.circuit.library import GraphState - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization import rustworkx as rx G = rx.generators.cycle_graph(5) circuit = GraphState(rx.adjacency_matrix(G)) diff --git a/qiskit/circuit/library/hidden_linear_function.py b/qiskit/circuit/library/hidden_linear_function.py index 4742145ab51f..1140f1866f08 100644 --- a/qiskit/circuit/library/hidden_linear_function.py +++ b/qiskit/circuit/library/hidden_linear_function.py @@ -56,7 +56,7 @@ class HiddenLinearFunction(QuantumCircuit): .. plot:: from qiskit.circuit.library import HiddenLinearFunction - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization A = [[1, 1, 0], [1, 0, 1], [0, 1, 1]] circuit = HiddenLinearFunction(A) _generate_circuit_library_visualization(circuit) diff --git a/qiskit/circuit/library/iqp.py b/qiskit/circuit/library/iqp.py index e18c49d531d0..db65fbe294ad 100644 --- a/qiskit/circuit/library/iqp.py +++ b/qiskit/circuit/library/iqp.py @@ -47,7 +47,7 @@ class IQP(QuantumCircuit): .. plot:: from qiskit.circuit.library import IQP - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization A = [[6, 5, 3], [5, 4, 5], [3, 5, 1]] circuit = IQP(A) _generate_circuit_library_visualization(circuit.decompose()) diff --git a/qiskit/circuit/library/phase_estimation.py b/qiskit/circuit/library/phase_estimation.py index d513bf2ad3af..3311adf46324 100644 --- a/qiskit/circuit/library/phase_estimation.py +++ b/qiskit/circuit/library/phase_estimation.py @@ -74,7 +74,7 @@ def __init__( from qiskit.circuit import QuantumCircuit from qiskit.circuit.library import PhaseEstimation - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization unitary = QuantumCircuit(2) unitary.x(0) unitary.y(1) diff --git a/qiskit/circuit/library/quantum_volume.py b/qiskit/circuit/library/quantum_volume.py index 43340dfff3ce..54a1b30dbec7 100644 --- a/qiskit/circuit/library/quantum_volume.py +++ b/qiskit/circuit/library/quantum_volume.py @@ -43,7 +43,7 @@ class QuantumVolume(QuantumCircuit): .. plot:: from qiskit.circuit.library import QuantumVolume - from qiskit.tools.jupyter.library import _generate_circuit_library_visualization + from qiskit.visualization.library import _generate_circuit_library_visualization circuit = QuantumVolume(5, 6, seed=10, classical_permutation=False) _generate_circuit_library_visualization(circuit.decompose()) diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 4cfc06d153a4..698f04b8513b 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -3351,32 +3351,9 @@ def delay( Raises: CircuitError: if arguments have bad format. """ - qubits: list[QubitSpecifier] = [] - if qarg is None: # -> apply delays to all qubits - for q in self.qubits: - qubits.append(q) - else: - if isinstance(qarg, QuantumRegister): - qubits.extend([qarg[j] for j in range(qarg.size)]) - elif isinstance(qarg, list): - qubits.extend(qarg) - elif isinstance(qarg, (range, tuple)): - qubits.extend(list(qarg)) - elif isinstance(qarg, slice): - qubits.extend(self.qubits[qarg]) - else: - qubits.append(qarg) - - instructions = InstructionSet( - resource_requester=self._current_scope().resolve_classical_resource - ) - for q in qubits: - inst: tuple[ - Instruction, Sequence[QubitSpecifier] | None, Sequence[ClbitSpecifier] | None - ] = (Delay(duration, unit), [q], []) - self.append(*inst) - instructions.add(*inst) - return instructions + if qarg is None: + qarg = self.qubits + return self.append(Delay(duration, unit=unit), [qarg], []) def h(self, qubit: QubitSpecifier) -> InstructionSet: """Apply :class:`~qiskit.circuit.library.HGate`. diff --git a/qiskit/exceptions.py b/qiskit/exceptions.py index 048f79dfbc62..e26d4097fa90 100644 --- a/qiskit/exceptions.py +++ b/qiskit/exceptions.py @@ -15,7 +15,10 @@ Top-level exceptions (:mod:`qiskit.exceptions`) =============================================== -All Qiskit-related errors raised by Qiskit are subclasses of the base: +Exceptions +========== + +All Qiskit-related exceptions raised by Qiskit are subclasses of the base: .. autoexception:: QiskitError @@ -42,6 +45,22 @@ .. autoexception:: QiskitUserConfigError .. autoexception:: InvalidFileError + + +Warnings +======== + +Some particular features of Qiskit may raise custom warnings. In general, Qiskit will use built-in +Python warnings (such as :exc:`DeprecationWarning`) when appropriate, but warnings related to +Qiskit-specific functionality will be subtypes of :exc:`QiskitWarning`. + +.. autoexception:: QiskitWarning + +Related to :exc:`MissingOptionalLibraryError`, in some cases an optional dependency might be found, +but fail to import for some other reason. In this case, Qiskit will continue as if the dependency +is not present, but will raise :exc:`OptionalDependencyImportWarning` to let you know about it. + +.. autoexception:: OptionalDependencyImportWarning """ from typing import Optional @@ -95,3 +114,13 @@ def __str__(self) -> str: class InvalidFileError(QiskitError): """Raised when the file provided is not valid for the specific task.""" + + +class QiskitWarning(UserWarning): + """Common subclass of warnings for Qiskit-specific warnings being raised.""" + + +class OptionalDependencyImportWarning(QiskitWarning): + """Raised when an optional library raises errors during its import.""" + + # Not a subclass of `ImportWarning` because those are hidden by default. diff --git a/qiskit/passmanager/__init__.py b/qiskit/passmanager/__init__.py index 579a8569b750..a84f37abbf03 100644 --- a/qiskit/passmanager/__init__.py +++ b/qiskit/passmanager/__init__.py @@ -209,7 +209,6 @@ def digit_condition(property_set): .. autosummary:: :toctree: ../stubs/ - FlowController FlowControllerLinear ConditionalController DoWhileController @@ -232,7 +231,6 @@ def digit_condition(property_set): from .passmanager import BasePassManager from .flow_controllers import ( - FlowController, FlowControllerLinear, ConditionalController, DoWhileController, diff --git a/qiskit/passmanager/flow_controllers.py b/qiskit/passmanager/flow_controllers.py index eb06202e8673..c7d952d048dd 100644 --- a/qiskit/passmanager/flow_controllers.py +++ b/qiskit/passmanager/flow_controllers.py @@ -15,9 +15,8 @@ import logging from collections.abc import Callable, Iterable, Generator -from typing import Type, Any +from typing import Any -from qiskit.utils.deprecation import deprecate_func from .base_tasks import BaseController, Task from .compilation_status import PassManagerState, PropertySet from .exceptions import PassManagerError @@ -45,31 +44,6 @@ def passes(self) -> list[Task]: """Alias of tasks for backward compatibility.""" return list(self.tasks) - @deprecate_func( - since="0.45.0", - additional_msg="All tasks must be provided at construction time of the controller object.", - ) - def append( - self, - passes: Task | list[Task], - ): - """Add new task to pipeline. - - Args: - passes: A new task or list of tasks to add. - """ - if not isinstance(passes, Iterable): - passes = [passes] - - tasks = list(self.tasks) - for task in passes: - if not isinstance(task, Task): - raise TypeError( - f"New task {task} is not a valid pass manager pass or flow controller." - ) - tasks.append(task) - self.tasks = tuple(tasks) - def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerState, None]: for task in self.tasks: state = yield task @@ -101,31 +75,6 @@ def passes(self) -> list[Task]: """Alias of tasks for backward compatibility.""" return list(self.tasks) - @deprecate_func( - since="0.45.0", - additional_msg="All tasks must be provided at construction time of the controller object.", - ) - def append( - self, - passes: Task | list[Task], - ): - """Add new task to pipeline. - - Args: - passes: A new task or list of tasks to add. - """ - if not isinstance(passes, Iterable): - passes = [passes] - - tasks = list(self.tasks) - for task in passes: - if not isinstance(task, Task): - raise TypeError( - f"New task {task} is not a valid pass manager pass or flow controller." - ) - tasks.append(task) - self.tasks = tuple(tasks) - def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerState, None]: max_iteration = self._options.get("max_iteration", 1000) for _ in range(max_iteration): @@ -161,176 +110,7 @@ def passes(self) -> list[Task]: """Alias of tasks for backward compatibility.""" return list(self.tasks) - @deprecate_func( - since="0.45.0", - additional_msg="All tasks must be provided at construction time of the controller object.", - ) - def append( - self, - passes: Task | list[Task], - ): - """Add new task to pipeline. - - Args: - passes: A new task or list of tasks to add. - """ - if not isinstance(passes, Iterable): - passes = [passes] - - tasks = list(self.tasks) - for task in passes: - if not isinstance(task, Task): - raise TypeError( - f"New task {task} is not a valid pass manager pass or flow controller." - ) - tasks.append(task) - self.tasks = tuple(tasks) - def iter_tasks(self, state: PassManagerState) -> Generator[Task, PassManagerState, None]: if self.condition(state.property_set): for task in self.tasks: state = yield task - - -class FlowController(BaseController): - """A legacy factory for other flow controllers. - - .. warning:: - - This class is primarily for compatibility with legacy versions of Qiskit, and in general, - you should prefer simply instantiating the controller you want, and adding it to the - relevant :class:`.PassManager` or other controller. Its use is deprecated. - - This allows syntactic sugar for writing pipelines. For example:: - - FlowController.add_flow_controller("my_condition", CustomController) - - controller = FlowController.controller_factory( - [PassA(), PassB()], - {"max_iteration": 1000}, - condition=lambda prop_set: prop_set["x"] == 0, - do_while=lambda prop_set: prop_set["x"] < 100, - my_condition=lambda prop_set: prop_set["y"] = "abc", - ) - - This creates a nested flow controller that runs when the value :code:`x` in the - :class:`.PropertySet` is zero and repeats the pipeline until the value becomes 100. - In each innermost loop, the custom iteration condition provided by - the ``CustomController`` is also evaluated. - - .. warning:: - - :class:`.BaseController` must be directly subclassed to define a custom flow controller. - This class provides a controller factory method, which consumes a class variable - :attr:`.registered_controllers`. Subclassing FlowController may cause - unexpected behavior in the factory method. - Note that factory method implicitly determines the priority of the builtin controllers - when multiple controllers are called together, - and the behavior of generated controller is hardly debugged. - """ - - registered_controllers = { - "condition": ConditionalController, - "do_while": DoWhileController, - } - hierarchy = [ - "condition", - "do_while", - ] - - @classmethod - @deprecate_func( - since="0.45.0", - additional_msg=( - "Controller object must be explicitly instantiated. " - "Building controller with keyword arguments may yield race condition when " - "multiple keyword arguments are provided together, which is likely unsafe." - ), - ) - def controller_factory( - cls, - passes: Task | list[Task], - options: dict, - **controllers, - ): - """Create a new flow controller with normalization. - - Args: - passes: A list of optimization tasks. - options: Option for this flow controller. - controllers: Dictionary of controller callables keyed on flow controller alias. - - Returns: - An instance of normalized flow controller. - """ - if None in controllers.values(): - raise PassManagerError("The controller needs a callable. Value cannot be None.") - - if isinstance(passes, BaseController): - instance = passes - else: - instance = FlowControllerLinear(passes, options=options) - - if controllers: - # Alias in higher hierarchy becomes outer controller. - for alias in cls.hierarchy[::-1]: - if alias not in controllers: - continue - class_type = cls.registered_controllers[alias] - init_kwargs = { - "options": options, - alias: controllers.pop(alias), - } - instance = class_type([instance], **init_kwargs) - - return instance - - @classmethod - @deprecate_func( - since="0.45.0", - additional_msg=( - "Controller factory method is deprecated and managing the custom flow controllers " - "with alias no longer helps building the task pipeline. " - "Controllers must be explicitly instantiated and appended to the pipeline." - ), - ) - def add_flow_controller( - cls, - name: str, - controller: Type[BaseController], - ): - """Adds a flow controller. - - Args: - name: Alias of controller class in the namespace. - controller: Flow controller class. - """ - cls.registered_controllers[name] = controller - if name not in cls.hierarchy: - cls.hierarchy.append(name) - - @classmethod - @deprecate_func( - since="0.45.0", - additional_msg=( - "Controller factory method is deprecated and managing the custom flow controllers " - "with alias no longer helps building the task pipeline. " - "Controllers must be explicitly instantiated and appended to the pipeline." - ), - ) - def remove_flow_controller( - cls, - name: str, - ): - """Removes a flow controller. - - Args: - name: Alias of the controller to remove. - - Raises: - KeyError: If the controller to remove was not registered. - """ - if name not in cls.hierarchy: - raise KeyError("Flow controller not found: %s" % name) - del cls.registered_controllers[name] - cls.hierarchy.remove(name) diff --git a/qiskit/primitives/__init__.py b/qiskit/primitives/__init__.py index cc12c9fbf083..ecc24757f815 100644 --- a/qiskit/primitives/__init__.py +++ b/qiskit/primitives/__init__.py @@ -50,13 +50,15 @@ EstimatorResult SamplerResult + PrimitiveResult + PubResult """ -from .base import BaseEstimator -from .base import BaseSampler from .backend_estimator import BackendEstimator -from .estimator import Estimator -from .base.estimator_result import EstimatorResult from .backend_sampler import BackendSampler -from .sampler import Sampler +from .base import BaseEstimator, BaseSampler +from .base.estimator_result import EstimatorResult from .base.sampler_result import SamplerResult +from .containers import BindingsArray, ObservablesArray, PrimitiveResult, PubResult +from .estimator import Estimator +from .sampler import Sampler diff --git a/qiskit/tools/monitor/__init__.py b/qiskit/primitives/containers/__init__.py similarity index 62% rename from qiskit/tools/monitor/__init__.py rename to qiskit/primitives/containers/__init__.py index cd0f22b90e48..5367d4594008 100644 --- a/qiskit/tools/monitor/__init__.py +++ b/qiskit/primitives/containers/__init__.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2018. +# (C) Copyright IBM 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -10,7 +10,12 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""A module for monitoring jobs, backends, etc.""" +""" +Data containers for primitives. +""" -from .job_monitor import job_monitor -from .overview import backend_monitor, backend_overview +from .bindings_array import BindingsArray +from .data_bin import make_data_bin +from .observables_array import ObservablesArray +from .primitive_result import PrimitiveResult +from .pub_result import PubResult diff --git a/qiskit/primitives/containers/bindings_array.py b/qiskit/primitives/containers/bindings_array.py new file mode 100644 index 000000000000..14efabdc396d --- /dev/null +++ b/qiskit/primitives/containers/bindings_array.py @@ -0,0 +1,354 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Bindings array class +""" +from __future__ import annotations + +from collections.abc import Iterable, Mapping, Sequence +from itertools import chain +from typing import Union + +import numpy as np +from numpy.typing import ArrayLike + +from qiskit.circuit import Parameter, QuantumCircuit +from qiskit.circuit.parameterexpression import ParameterValueType + +from .shape import ShapedMixin, ShapeInput, shape_tuple + +ParameterLike = Union[Parameter, str] + + +class BindingsArray(ShapedMixin): + r"""Stores parameter binding value sets for a :class:`qiskit.QuantumCircuit`. + + A single parameter binding set provides numeric values to bind to a circuit with free + :class:`qiskit.circuit.Parameter`\s. An instance of this class stores an array-valued + collection of such sets. The simplest example is a 0-d array consisting of a single + parameter binding set, whereas an n-d array of parameter binding sets represents an + n-d sweep over values. + + The storage format is a list of arrays, ``[vals0, vals1, ...]``, as well as a dictionary of + arrays attached to parameters, ``{params0: kwvals0, ...}``. A convention is used + where the last dimension of each array indexes (a subset of) circuit parameters. For + example, if the last dimension of ``vals1`` is 25, then it represents an array of + possible binding values for 25 distinct parameters, where its leading shape is the + array :attr:`~.shape` of its binding array. This implies a degeneracy of the storage + format: ``[vals, vals1[..., :10], vals1[..., 10:], ...]`` is exactly equivalent to + ``[vals0, vals1, ...]`` in the bindings it specifies. This allows flexibility about whether + values for different parameters are stored in one big array, or across several smaller + arrays. It also allows different parameters to use different dtypes. + + .. code-block:: python + + # 0-d array (i.e. only one binding) + BindingsArray([1, 2, 3], {"a": 4, ("b", "c"): [5, 6]}) + + # single array, last index is parameters + BindingsArray(np.empty((10, 10, 100))) + + # multiple arrays, where each last index is parameters. notice that it's smart enough to + # figure out that a missing last dimension corresponds to a single parameter. + BindingsArray( + [np.empty((10, 10, 100)), np.empty((10, 10)), np.empty((10, 10, 20), dtype=complex)], + {("c", "a"): np.empty((10, 10, 2)), "b": np.empty((10, 10))} + ) + """ + __slots__ = ("_vals", "_kwvals") + + def __init__( + self, + vals: ArrayLike | Iterable[ArrayLike] | None = None, + kwvals: Mapping[ParameterLike, Iterable[ParameterValueType]] | ArrayLike | None = None, + shape: ShapeInput | None = None, + ): + r""" + Initialize a ``BindingsArray``. It can take parameter vectors and dictionaries. + + The ``shape`` argument does not need to be provided whenever it can unambiguously + be inferred from the provided arrays. Ambiguity arises because an array provided to the + constructor might represent values for either a single parameter, with an implicit missing + last dimension of size ``1``, or for many parameters, where the size of the last dimension + is the number of parameters it is providing values to. This ambiguity can be broken in the + following common ways: + + * Only a single array is provided to ``vals``, and no arrays to ``kwvals``, in which case + it is assumed that the last dimension is over many parameters. + * Multiple arrays are given whose shapes differ only in the last dimension size. + * Some array is given in ``kwvals`` where the key contains multiple + :class:`~.Parameter`\s, whose length the last dimension of the array must therefore match. + + Args: + vals: One or more arrays, where the last index of each corresponds to + distinct parameters. If their dtypes allow it, concatenating these + arrays over the last axis is equivalent to providing them separately. + kwvals: A mapping from one or more parameters to arrays of values to bind + them to, where the last axis is over parameters. + shape: The leading shape of every array in these bindings. + + Raises: + ValueError: If all inputs are ``None``. + ValueError: If the shape cannot be automatically inferred from the arrays, or if there + is some inconsistency in the shape of the given arrays. + """ + super().__init__() + + if vals is None: + vals = [] + if kwvals is None: + kwvals = {} + + vals = [vals] if isinstance(vals, np.ndarray) else [np.array(v, copy=False) for v in vals] + # TODO str will be used for internal data (_kwvals) instead of Parameter. + # This requires https://github.com/Qiskit/qiskit/issues/7107 + kwvals = { + (p,) if isinstance(p, Parameter) else tuple(p): np.array(val, copy=False) + for p, val in kwvals.items() + } + + if shape is None: + # jump through hoops to find out user's intended shape + shape = _infer_shape(vals, kwvals) + + # shape checking, and normalization so that each last index must be over parameters + self._shape = shape_tuple(shape) + for idx, val in enumerate(vals): + vals[idx] = _standardize_shape(val, self._shape) + + self._vals: list[np.ndarray] = vals + self._kwvals = kwvals + + self.validate() + + def __getitem__(self, args) -> BindingsArray: + # because the parameters live on the last axis, we don't need to do anything special to + # accomodate them because there will always be an implicit slice(None, None, None) + # on all unspecified trailing dimensions + # separately, we choose to not disallow args which touch the last dimension, even though it + # would not be a particularly friendly way to chop parameters + vals = [val[args] for val in self._vals] + kwvals = {params: val[args] for params, val in self._kwvals.items()} + try: + shape = next(chain(vals, kwvals.values())).shape[:-1] + except StopIteration: + shape = () + return BindingsArray(vals, kwvals, shape) + + @property + def kwvals(self) -> dict[tuple[str, ...], np.ndarray]: + """The keyword values of this array.""" + return {_format_key(k): v for k, v in self._kwvals.items()} + + @property + def num_parameters(self) -> int: + """The total number of parameters.""" + return sum(val.shape[-1] for val in chain(self.vals, self._kwvals.values())) + + @property + def vals(self) -> list[np.ndarray]: + """The non-keyword values of this array.""" + return self._vals + + def bind(self, circuit: QuantumCircuit, loc: tuple[int, ...]) -> QuantumCircuit: + """Return a new circuit bound to the values at the provided index. + + Args: + circuit: The circuit to bind. + loc: A tuple of indices, on for each dimension of this array. + + Returns: + The bound circuit. + + Raises: + ValueError: If the index doesn't have the right number of values. + """ + if len(loc) != self.ndim: + raise ValueError(f"Expected {loc} to index all dimensions of {self.shape}") + + flat_vals = (val for vals in self.vals for val in vals[loc]) + + if not self._kwvals: + # special case to avoid constructing a dictionary input + return circuit.assign_parameters(list(flat_vals)) + + parameters = dict(zip(circuit.parameters, flat_vals)) + parameters.update( + (param, val) + for params, vals in self._kwvals.items() + for param, val in zip(params, vals[loc]) + ) + return circuit.assign_parameters(parameters) + + def bind_all(self, circuit: QuantumCircuit) -> np.ndarray: + """Return an object array of bound circuits with the same shape. + + Args: + circuit: The circuit to bind. + + Returns: + An object array of the same shape containing all bound circuits. + """ + arr = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + arr[idx] = self.bind(circuit, idx) + return arr + + def ravel(self) -> BindingsArray: + """Return a new :class:`~BindingsArray` with one dimension. + + The returned bindings array has a :attr:`shape` given by ``(size, )``, where the size is the + :attr:`~size` of this bindings array. + + Returns: + A new bindings array. + """ + return self.reshape(self.size) + + def reshape(self, shape: int | Iterable[int]) -> BindingsArray: + """Return a new :class:`~BindingsArray` with a different shape. + + This results in a new view of the same arrays. + + Args: + shape: The shape of the returned bindings array. + + Returns: + A new bindings array. + + Raises: + ValueError: If the provided shape has a different product than the current size. + """ + shape = (shape, -1) if isinstance(shape, int) else (*shape, -1) + if np.prod(shape[:-1]).astype(int) != self.size: + raise ValueError("Reshaping cannot change the total number of elements.") + vals = [val.reshape(shape) for val in self._vals] + kwvals = {params: val.reshape(shape) for params, val in self._kwvals.items()} + return BindingsArray(vals, kwvals, shape[:-1]) + + @classmethod + def coerce(cls, bindings_array: BindingsArrayLike) -> BindingsArray: + """Coerce an input that is :class:`~BindingsArrayLike` into a new :class:`~BindingsArray`. + + Args: + bindings_array: An object to be bindings array. + + Returns: + A new bindings array. + """ + if isinstance(bindings_array, Sequence): + bindings_array = np.array(bindings_array) + if bindings_array is None: + bindings_array = cls() + elif isinstance(bindings_array, np.ndarray): + bindings_array = cls(bindings_array) + elif isinstance(bindings_array, Mapping): + bindings_array = cls(kwvals=bindings_array) + else: + raise TypeError(f"Unsupported type {type(bindings_array)} is given.") + return bindings_array + + def validate(self): + """Validate the consistency in bindings_array.""" + for parameters, val in self._kwvals.items(): + val = self._kwvals[parameters] = _standardize_shape(val, self._shape) + if len(parameters) != val.shape[-1]: + raise ValueError( + f"Length of {parameters} inconsistent with last dimension of {val}" + ) + + +def _standardize_shape(val: np.ndarray, shape: tuple[int, ...]) -> np.ndarray: + """Return ``val`` or ``val[..., None]``. + + Args: + val: The array whose shape to standardize. + shape: The shape to standardize to. + + Returns: + An array with one more dimension than ``len(shape)``, and whose leading dimensions match + ``shape``. + + Raises: + ValueError: If the leading shape of ``val`` does not match the ``shape``. + """ + if val.shape == shape: + val = val[..., None] + elif val.ndim - 1 != len(shape) or val.shape[:-1] != shape: + raise ValueError(f"Array with shape {val.shape} inconsistent with {shape}") + return val + + +def _infer_shape( + vals: list[np.ndarray], kwvals: dict[tuple[Parameter, ...], np.ndarray] +) -> tuple[int, ...]: + """Return a shape tuple that consistently defines the leading dimensions of all arrays. + + Args: + vals: A list of arrays. + kwvals: A mapping from tuples to arrays, where the length of each tuple should match the + last dimension of the corresponding array. + + Returns: + A shape tuple that matches the leading dimension of every array. + + Raises: + ValueError: If this cannot be done unambiguously. + """ + only_possible_shapes = None + + def examine_array(*possible_shapes): + nonlocal only_possible_shapes + if only_possible_shapes is None: + only_possible_shapes = set(possible_shapes) + else: + only_possible_shapes.intersection_update(possible_shapes) + + for parameters, val in kwvals.items(): + if len(parameters) > 1: + # here, the last dimension _has_ to be over parameters + examine_array(val.shape[:-1]) + elif val.shape == () or val.shape == (1,) or val.shape[-1] != 1: + # here, if the last dimension is not 1 or shape is () or (1,) then the shape is the shape + examine_array(val.shape) + else: + # here, the last dimension could be over parameters or not + examine_array(val.shape, val.shape[:-1]) + + if len(vals) == 1 and len(kwvals) == 0: + examine_array(vals[0].shape[:-1]) + elif len(vals) == 0 and len(kwvals) == 0: + examine_array(()) + else: + for val in vals: + # here, the last dimension could be over parameters or not + examine_array(val.shape, val.shape[:-1]) + + if len(only_possible_shapes) == 1: + return next(iter(only_possible_shapes)) + elif len(only_possible_shapes) == 0: + raise ValueError("Could not find any consistent shape.") + raise ValueError("Could not unambiguously determine the intended shape; specify shape manually") + + +def _format_key(key: tuple[Parameter | str, ...]): + return tuple(k.name if isinstance(k, Parameter) else k for k in key) + + +BindingsArrayLike = Union[ + BindingsArray, + ArrayLike, + "Mapping[Parameter, ArrayLike]", + "Sequence[ArrayLike]", + None, +] diff --git a/qiskit/primitives/containers/data_bin.py b/qiskit/primitives/containers/data_bin.py new file mode 100644 index 000000000000..b4e479266e72 --- /dev/null +++ b/qiskit/primitives/containers/data_bin.py @@ -0,0 +1,89 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Dataclass tools for data namespaces (bins) +""" +from __future__ import annotations + +from collections.abc import Iterable +from dataclasses import make_dataclass + + +class DataBinMeta(type): + """Metaclass for :class:`DataBin` that adds the shape to the type name. + + This is so that the class has a custom repr with DataBin<*shape> notation. + """ + + def __repr__(cls): + name = cls.__name__ + if cls._SHAPE is None: + return name + shape = ",".join(map(str, cls._SHAPE)) + return f"{name}<{shape}>" + + +class DataBin(metaclass=DataBinMeta): + """Base class for data bin containers. + + Subclasses are typically made via :class:`~make_data_bin`, which is a specialization of + :class:`make_dataclass`. + """ + + _RESTRICTED_NAMES = ("_RESTRICTED_NAMES", "_SHAPE", "_FIELDS", "_FIELD_TYPES") + _SHAPE: tuple[int, ...] | None = None + _FIELDS: tuple[str, ...] = () + """The fields allowed in this data bin.""" + _FIELD_TYPES: tuple[type, ...] = () + """The types of each field.""" + + def __repr__(self): + vals = (f"{name}={getattr(self, name)}" for name in self._FIELDS if hasattr(self, name)) + return f"{type(self)}({', '.join(vals)})" + + +def make_data_bin( + fields: Iterable[tuple[str, type]], shape: tuple[int, ...] | None = None +) -> DataBinMeta: + """Return a new subclass of :class:`~DataBin` with the provided fields and shape. + + .. code-block:: python + + my_bin = make_data_bin([("alpha", np.NDArray[np.float])], shape=(20, 30)) + + # behaves like a dataclass + my_bin(alpha=np.empty((20, 30))) + + Args: + fields: Tuples ``(name, type)`` specifying the attributes of the returned class. + shape: The intended shape of every attribute of this class. + + Returns: + A new class. + """ + field_names, field_types = zip(*fields) + for name in field_names: + if name in DataBin._RESTRICTED_NAMES: + raise ValueError(f"'{name}' is a restricted name for a DataBin.") + cls = make_dataclass( + "DataBin", + dict(zip(field_names, field_types)), + bases=(DataBin,), + frozen=True, + unsafe_hash=True, + repr=False, + ) + cls._SHAPE = shape + cls._FIELDS = field_names + cls._FIELD_TYPES = field_types + return cls diff --git a/qiskit/primitives/containers/object_array.py b/qiskit/primitives/containers/object_array.py new file mode 100644 index 000000000000..f93515daf898 --- /dev/null +++ b/qiskit/primitives/containers/object_array.py @@ -0,0 +1,94 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Object ND-array initialization function. +""" +from __future__ import annotations + +from collections.abc import Sequence + +import numpy as np +from numpy.typing import ArrayLike + + +def object_array( + arr: ArrayLike, + order: str | None = None, + copy: bool = True, + list_types: Sequence[type] | None = (), +) -> np.ndarray: + """Convert an array-like of objects into an object array. + + .. note:: + + If the objects in the array like input define ``__array__`` methods + this avoids calling them and will instead set the returned array values + to the Python objects themselves. + + Args: + arr: An array-like input. + order: Optional, the order of the returned array (C, F, A, K). If None + the default NumPy ordering of C is used. + copy: If True make a copy of the input if it is already an array. + list_types: Optional, a sequence of types to treat as lists of array + element objects when inferring the array shape from the input. + + Returns: + A NumPy ND-array with ``dtype=object``. + + Raises: + ValueError: If the input cannot be coerced into an object array. + """ + if isinstance(arr, np.ndarray): + if arr.dtype != object or order is not None or copy is True: + arr = arr.astype(object, order=order, copy=copy) + return arr + + shape = _infer_shape(arr, list_types=tuple(list_types)) + obj_arr = np.empty(shape, dtype=object, order=order) + if not shape: + # We call fill here instead of [()] to avoid invoking the + # objects `__array__` method if it has one (eg for Pauli's). + obj_arr.fill(arr) + else: + # For other arrays we need to do some tricks to avoid invoking the + # objects __array__ method by flattening the input and initializing + # using `np.fromiter` which does not invoke `__array__` for object + # dtypes. + def _flatten(nested, k): + if k == 1: + return nested + else: + return [item for sublist in nested for item in _flatten(sublist, k - 1)] + + flattened = _flatten(arr, len(shape)) + if len(flattened) != obj_arr.size: + raise ValueError( + "Input object size does not match the inferred array shape." + " This most likely occurs when the input is a ragged array." + ) + obj_arr.flat = np.fromiter(flattened, dtype=object, count=len(flattened)) + + return obj_arr + + +def _infer_shape(obj: ArrayLike, list_types: tuple[type, ...] = ()) -> tuple[int, ...]: + """Infer the shape of an array-like object without casting""" + if isinstance(obj, np.ndarray): + return obj.shape + if not isinstance(obj, (list, *list_types)): + return () + size = len(obj) + if size == 0: + return (size,) + return (size, *_infer_shape(obj[0], list_types=list_types)) diff --git a/qiskit/primitives/containers/observables_array.py b/qiskit/primitives/containers/observables_array.py new file mode 100644 index 000000000000..0d33111d5957 --- /dev/null +++ b/qiskit/primitives/containers/observables_array.py @@ -0,0 +1,265 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + + +""" +ND-Array container class for Estimator observables. +""" +from __future__ import annotations + +import re +from collections import defaultdict +from collections.abc import Mapping as MappingType +from functools import lru_cache +from typing import Iterable, Mapping, Union, overload + +import numpy as np +from numpy.typing import ArrayLike + +from qiskit.quantum_info import Pauli, PauliList, SparsePauliOp + +from .object_array import object_array +from .shape import ShapedMixin + +BasisObservable = Mapping[str, complex] +"""Representation type of a single observable.""" + +BasisObservableLike = Union[ + str, + Pauli, + SparsePauliOp, + Mapping[Union[str, Pauli], complex], + Iterable[Union[str, Pauli, SparsePauliOp]], +] +"""Types that can be natively used to construct a :const:`BasisObservable`.""" + + +class ObservablesArray(ShapedMixin): + """An ND-array of :const:`.BasisObservable` for an :class:`.Estimator` primitive.""" + + __slots__ = ("_array", "_shape") + ALLOWED_BASIS: str = "IXYZ01+-lr" + """The allowed characters in :const:`BasisObservable` strings.""" + + def __init__( + self, + observables: BasisObservableLike | ArrayLike, + copy: bool = True, + validate: bool = True, + ): + """Initialize an observables array. + + Args: + observables: An array-like of basis observable compatible objects. + copy: Specify the ``copy`` kwarg of the :func:`.object_array` function + when initializing observables. + validate: If True, convert :const:`.BasisObservableLike` input objects + to :const:`.BasisObservable` objects and validate. If False the + input should already be an array-like of valid + :const:`.BasisObservble` objects. + + Raises: + ValueError: If ``validate=True`` and the input observables is not valid. + """ + super().__init__() + if isinstance(observables, ObservablesArray): + observables = observables._array + self._array = object_array(observables, copy=copy, list_types=(PauliList,)) + self._shape = self._array.shape + if validate: + num_qubits = None + for ndi, obs in np.ndenumerate(self._array): + basis_obs = self.format_observable(obs) + basis_num_qubits = len(next(iter(basis_obs))) + if num_qubits is None: + num_qubits = basis_num_qubits + elif basis_num_qubits != num_qubits: + raise ValueError( + "The number of qubits must be the same for all observables in the " + "observables array." + ) + self._array[ndi] = basis_obs + + def __repr__(self): + prefix = f"{type(self).__name__}(" + suffix = f", shape={self.shape})" + array = np.array2string(self._array, prefix=prefix, suffix=suffix, threshold=50) + return prefix + array + suffix + + def tolist(self) -> list: + """Convert to a nested list""" + return self._array.tolist() + + def __array__(self, dtype=None): + """Convert to an Numpy.ndarray""" + if dtype is None or dtype == object: + return self._array + raise ValueError("Type must be 'None' or 'object'") + + @overload + def __getitem__(self, args: int | tuple[int, ...]) -> BasisObservable: + ... + + @overload + def __getitem__(self, args: slice) -> ObservablesArray: + ... + + def __getitem__(self, args): + item = self._array[args] + if not isinstance(item, np.ndarray): + return item + return ObservablesArray(item, copy=False, validate=False) + + def reshape(self, shape: int | Iterable[int]) -> ObservablesArray: + """Return a new array with a different shape. + + This results in a new view of the same arrays. + + Args: + shape: The shape of the returned array. + + Returns: + A new array. + """ + return ObservablesArray(self._array.reshape(shape), copy=False, validate=False) + + def ravel(self) -> ObservablesArray: + """Return a new array with one dimension. + + The returned array has a :attr:`shape` given by ``(size, )``, where + the size is the :attr:`~size` of this array. + + Returns: + A new flattened array. + """ + return self.reshape(self.size) + + @classmethod + def format_observable(cls, observable: BasisObservableLike) -> BasisObservable: + """Format an observable-like object into a :const:`BasisObservable`. + + Args: + observable: The observable-like to format. + + Returns: + The given observable as a :const:`~BasisObservable`. + + Raises: + TypeError: If the input cannot be formatted because its type is not valid. + ValueError: If the input observable is invalid. + """ + + # Pauli-type conversions + if isinstance(observable, SparsePauliOp): + # Call simplify to combine duplicate keys before converting to a mapping + return cls.format_observable(dict(observable.simplify(atol=0).to_list())) + + if isinstance(observable, Pauli): + label, phase = observable[:].to_label(), observable.phase + return {label: 1} if phase == 0 else {label: (-1j) ** phase} + + # String conversion + if isinstance(observable, str): + cls._validate_basis(observable) + return {observable: 1} + + # Mapping conversion (with possible Pauli keys) + if isinstance(observable, MappingType): + num_qubits = len(next(iter(observable))) + unique = defaultdict(complex) + for basis, coeff in observable.items(): + if isinstance(basis, Pauli): + basis, phase = basis[:].to_label(), basis.phase + if phase != 0: + coeff = coeff * (-1j) ** phase + # Validate basis + cls._validate_basis(basis) + if len(basis) != num_qubits: + raise ValueError( + "Number of qubits must be the same for all observable basis elements." + ) + unique[basis] += coeff + return dict(unique) + + raise TypeError(f"Invalid observable type: {type(observable)}") + + @classmethod + def coerce(cls, observables: ObservablesArrayLike) -> ObservablesArray: + """Coerce ObservablesArrayLike into ObservableArray. + + Args: + observables: an object to be observables array. + + Returns: + A coerced observables array. + """ + if isinstance(observables, ObservablesArray): + return observables + if isinstance(observables, (str, SparsePauliOp, Pauli, Mapping)): + observables = [observables] + return cls(observables) + + def validate(self): + """Validate the consistency in observables array.""" + num_qubits = None + for obs in self._array: + basis_num_qubits = len(next(iter(obs))) + if num_qubits is None: + num_qubits = basis_num_qubits + elif basis_num_qubits != num_qubits: + raise ValueError( + "The number of qubits must be the same for all observables in the " + "observables array." + ) + + @classmethod + def _validate_basis(cls, basis: str) -> None: + """Validate a basis string. + + Args: + basis: a basis string to validate. + + Raises: + ValueError: If basis string contains invalid characters + """ + # NOTE: the allowed basis characters can be overridden by modifying the class + # attribute ALLOWED_BASIS + allowed_pattern = _regex_match(cls.ALLOWED_BASIS) + if not allowed_pattern.match(basis): + invalid_pattern = _regex_invalid(cls.ALLOWED_BASIS) + invalid_chars = list(set(invalid_pattern.findall(basis))) + raise ValueError( + f"Observable basis string '{basis}' contains invalid characters {invalid_chars}," + f" allowed characters are {list(cls.ALLOWED_BASIS)}.", + ) + + +ObservablesArrayLike = Union[ObservablesArray, ArrayLike, BasisObservableLike] +"""Types that can be natively converted to an ObservablesArray""" + + +class PauliArray(ObservablesArray): + """An ND-array of Pauli-basis observables for an :class:`.Estimator` primitive.""" + + ALLOWED_BASIS = "IXYZ" + + +@lru_cache(1) +def _regex_match(allowed_chars: str) -> re.Pattern: + """Return pattern for matching if a string contains only the allowed characters.""" + return re.compile(f"^[{re.escape(allowed_chars)}]*$") + + +@lru_cache(1) +def _regex_invalid(allowed_chars: str) -> re.Pattern: + """Return pattern for selecting invalid strings""" + return re.compile(f"[^{re.escape(allowed_chars)}]") diff --git a/qiskit/primitives/containers/primitive_result.py b/qiskit/primitives/containers/primitive_result.py new file mode 100644 index 000000000000..b383f4e119e1 --- /dev/null +++ b/qiskit/primitives/containers/primitive_result.py @@ -0,0 +1,52 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""PrimitiveResult""" + +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any, Generic, TypeVar + +from .pub_result import PubResult + +T = TypeVar("T", bound=PubResult) + + +class PrimitiveResult(Generic[T]): + """A container for multiple pub results and global metadata.""" + + def __init__(self, pub_results: Iterable[T], metadata: dict[str, Any] | None = None): + """ + Args: + pub_results: Pub results. + metadata: Any metadata that doesn't make sense to put inside of pub results. + """ + self._pub_results = list(pub_results) + self._metadata = metadata or {} + + @property + def metadata(self) -> dict[str, Any]: + """The metadata of this primitive result.""" + return self._metadata + + def __getitem__(self, index) -> T: + return self._pub_results[index] + + def __len__(self) -> int: + return len(self._pub_results) + + def __repr__(self) -> str: + return f"PrimitiveResult({self._pub_results}, metadata={self.metadata})" + + def __iter__(self) -> Iterable[T]: + return iter(self._pub_results) diff --git a/qiskit/primitives/containers/pub_result.py b/qiskit/primitives/containers/pub_result.py new file mode 100644 index 000000000000..c7ac2b6160e7 --- /dev/null +++ b/qiskit/primitives/containers/pub_result.py @@ -0,0 +1,45 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Base Pub class +""" + +from __future__ import annotations + +from .data_bin import DataBin + + +class PubResult: + """Result of Primitive Unified Bloc.""" + + __slots__ = ("_data", "_metadata") + + def __init__(self, data: DataBin, metadata: dict | None = None): + """Initialize a pub result. + + Args: + data: result data bin. + metadata: metadata dictionary. + """ + self._data = data + self._metadata = metadata or {} + + @property + def data(self) -> DataBin: + """Result data for the pub.""" + return self._data + + @property + def metadata(self) -> dict: + """Metadata for the pub.""" + return self._metadata diff --git a/qiskit/primitives/containers/shape.py b/qiskit/primitives/containers/shape.py new file mode 100644 index 000000000000..952916cd67dc --- /dev/null +++ b/qiskit/primitives/containers/shape.py @@ -0,0 +1,129 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Array shape related classes and functions +""" +from __future__ import annotations + +from collections.abc import Iterable +from typing import Protocol, Union, runtime_checkable + +import numpy as np +from numpy.typing import ArrayLike, NDArray + +ShapeInput = Union[int, "Iterable[ShapeInput]"] +"""An input that is coercible into a shape tuple.""" + + +@runtime_checkable +class Shaped(Protocol): + """Protocol that defines what it means to be a shaped object. + + Note that static type checkers will classify ``numpy.ndarray`` as being :class:`Shaped`. + Moreover, since this protocol is runtime-checkable, we will even have + ``isinstance(, Shaped) == True``. + """ + + @property + def shape(self) -> tuple[int, ...]: + """The array shape of this object.""" + raise NotImplementedError("A `Shaped` protocol must implement the `shape` property") + + @property + def ndim(self) -> int: + """The number of array dimensions of this object.""" + raise NotImplementedError("A `Shaped` protocol must implement the `ndim` property") + + @property + def size(self) -> int: + """The total dimension of this object, i.e. the product of the entries of :attr:`~shape`.""" + raise NotImplementedError("A `Shaped` protocol must implement the `size` property") + + +class ShapedMixin(Shaped): + """Mixin class to create :class:`~Shaped` types by only providing :attr:`_shape` attribute.""" + + _shape: tuple[int, ...] + + def __repr__(self): + return f"{type(self).__name__}(<{self.shape}>)" + + @property + def shape(self): + return self._shape + + @property + def ndim(self): + return len(self._shape) + + @property + def size(self): + return int(np.prod(self._shape, dtype=int)) + + +def array_coerce(arr: ArrayLike | Shaped) -> NDArray | Shaped: + """Coerce the input into an object with a shape attribute. + + Copies are avoided. + + Args: + arr: The object to coerce. + + Returns: + Something that is :class:`~Shaped`, and always ``numpy.ndarray`` if the input is not + already :class:`~Shaped`. + """ + if isinstance(arr, Shaped): + return arr + return np.array(arr, copy=False) + + +def _flatten_to_ints(arg: ShapeInput) -> Iterable[int]: + """ + Yield one integer at a time. + + Args: + arg: Integers or iterables of integers, possibly nested, to be yielded. + + Yields: + The provided integers in depth-first recursive order. + + Raises: + ValueError: If an input is not an iterable or an integer. + """ + for item in arg: + try: + if isinstance(item, Iterable): + yield from _flatten_to_ints(item) + elif int(item) == item: + yield int(item) + else: + raise ValueError(f"Expected {item} to be iterable or an integer.") + except (TypeError, RecursionError) as ex: + raise ValueError(f"Expected {item} to be iterable or an integer.") from ex + + +def shape_tuple(*shapes: ShapeInput) -> tuple[int, ...]: + """ + Flatten the input into a single tuple of integers, preserving order. + + Args: + shapes: Integers or iterables of integers, possibly nested. + + Returns: + A tuple of integers. + + Raises: + ValueError: If some member of ``shapes`` is not an integer or iterable. + """ + return tuple(_flatten_to_ints(shapes)) diff --git a/qiskit/primitives/primitive_job.py b/qiskit/primitives/primitive_job.py index 7c87fd34994d..93924b2a4c23 100644 --- a/qiskit/primitives/primitive_job.py +++ b/qiskit/primitives/primitive_job.py @@ -15,13 +15,14 @@ import uuid from concurrent.futures import ThreadPoolExecutor -from typing import Generic, TypeVar +from typing import Generic, TypeVar, Union from qiskit.providers import JobError, JobStatus, JobV1 from .base.base_result import BasePrimitiveResult +from .containers import PrimitiveResult -T = TypeVar("T", bound=BasePrimitiveResult) +T = TypeVar("T", bound=Union[BasePrimitiveResult, PrimitiveResult]) class PrimitiveJob(JobV1, Generic[T]): diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index d7a52193dd15..5b9e8eb0477e 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -462,13 +462,12 @@ def get_translation_stage_plugin(self): handles submitting the circuits to the backend to be executed and returning a :class:`~qiskit.providers.Job` object. Depending on the type of backend this typically involves serializing the circuit object into the API format used by a -backend. For example, on IBMQ backends from the ``qiskit-ibmq-provider`` +backend. For example, on IBM backends from the ``qiskit-ibm-provider`` package this involves converting from a quantum circuit and options into a -`qobj `__ JSON payload and submitting -that to the IBM Quantum API. Since every backend interface is different (and -in the case of the local simulators serialization may not be needed) it is -expected that the backend's :obj:`~qiskit.providers.BackendV2.run` method will -handle this conversion. +:mod:`.qpy` payload embedded in JSON and submitting that to the IBM Quantum +API. Since every backend interface is different (and in the case of the local +simulators serialization may not be needed) it is expected that the backend's +:obj:`~qiskit.providers.BackendV2.run` method will handle this conversion. An example run method would be something like:: diff --git a/qiskit/providers/fake_provider/__init__.py b/qiskit/providers/fake_provider/__init__.py index 943869114f85..dae7311c68e0 100644 --- a/qiskit/providers/fake_provider/__init__.py +++ b/qiskit/providers/fake_provider/__init__.py @@ -65,21 +65,8 @@ Please note that the simulation is done using a noise model generated from system snapshots obtained in the past (sometimes a few years ago) and the results are not representative of the latest behaviours of the real quantum system which the fake backend is mimicking. If you want to - run noisy simulations to compare with the real quantum system, please follow steps below to - generate a simulator mimics a real quantum system with the latest calibration results. - - .. code-block:: python - - from qiskit.providers.ibmq import IBMQ - from qiskit.providers.aer import AerSimulator - - # get a real backend from a real provider - provider = IBMQ.load_account() - backend = provider.get_backend('ibmq_manila') - - # generate a simulator that mimics the real quantum system with the latest calibration results - backend_sim = AerSimulator.from_backend(backend) - + run noisy simulations to compare with the real quantum system you will need to create a noise + model from the current properties of the backend manually. Fake Providers ============== diff --git a/qiskit/providers/job.py b/qiskit/providers/job.py index fc797ba691b0..55ee2423e4e4 100644 --- a/qiskit/providers/job.py +++ b/qiskit/providers/job.py @@ -12,15 +12,20 @@ """Job abstract interface.""" +from __future__ import annotations + import time from abc import ABC, abstractmethod -from typing import Callable, Optional +from typing import Callable, Optional, TYPE_CHECKING from qiskit.exceptions import QiskitError from qiskit.providers.backend import Backend from qiskit.providers.exceptions import JobTimeoutError from qiskit.providers.jobstatus import JOB_FINAL_STATES, JobStatus +if TYPE_CHECKING: + from qiskit.result import Result + class Job: """Base common type for all versioned Job abstract classes. @@ -128,7 +133,7 @@ def submit(self): pass @abstractmethod - def result(self): + def result(self) -> Result: """Return the results of the job.""" pass @@ -137,6 +142,6 @@ def cancel(self): raise NotImplementedError @abstractmethod - def status(self): + def status(self) -> JobStatus: """Return the status of the job, among the values of ``JobStatus``.""" pass diff --git a/qiskit/qpy/exceptions.py b/qiskit/qpy/exceptions.py index 476e39eaa337..c6cdb4303a62 100644 --- a/qiskit/qpy/exceptions.py +++ b/qiskit/qpy/exceptions.py @@ -12,7 +12,7 @@ """Exception for errors raised by the QPY module.""" -from qiskit.exceptions import QiskitError +from qiskit.exceptions import QiskitError, QiskitWarning class QpyError(QiskitError): @@ -28,6 +28,6 @@ def __str__(self): return repr(self.message) -class QPYLoadingDeprecatedFeatureWarning(UserWarning): +class QPYLoadingDeprecatedFeatureWarning(QiskitWarning): """Visible deprecation warning for QPY loading functions without a stable point in the call stack.""" diff --git a/qiskit/test/__init__.py b/qiskit/test/__init__.py index 362eea2ba0ee..09a9a6c22d0f 100644 --- a/qiskit/test/__init__.py +++ b/qiskit/test/__init__.py @@ -13,6 +13,6 @@ """Functionality and helpers for testing Qiskit.""" from .base import QiskitTestCase -from .decorators import requires_aer_provider, online_test, slow_test +from .decorators import requires_aer_provider, slow_test from .reference_circuits import ReferenceCircuits from .utils import Path diff --git a/qiskit/test/base.py b/qiskit/test/base.py index 10cbb690eb58..67bc5874253f 100644 --- a/qiskit/test/base.py +++ b/qiskit/test/base.py @@ -28,6 +28,7 @@ import unittest from unittest.util import safe_repr +from qiskit.exceptions import QiskitWarning from qiskit.tools.parallel import get_platform_parallel_default from qiskit.utils import optionals as _optionals from qiskit.circuit import QuantumCircuit @@ -192,8 +193,6 @@ def tearDown(self): @classmethod def setUpClass(cls): super().setUpClass() - # Determines if the TestCase is using IBMQ credentials. - cls.using_ibmq_credentials = False # Set logging to file and stdout if the LOG_LEVEL envar is set. cls.log = logging.getLogger(cls.__name__) if os.getenv("LOG_LEVEL"): @@ -201,6 +200,8 @@ def setUpClass(cls): setup_test_logging(cls.log, os.getenv("LOG_LEVEL"), filename) warnings.filterwarnings("error", category=DeprecationWarning) + warnings.filterwarnings("error", category=QiskitWarning) + allow_DeprecationWarning_modules = [ "test.python.pulse.test_builder", "test.python.pulse.test_block", @@ -229,20 +230,6 @@ def setUpClass(cls): for msg in allow_DeprecationWarning_message: warnings.filterwarnings("default", category=DeprecationWarning, message=msg) - allow_aer_DeprecationWarning_message = [ - # This warning should be fixed once Qiskit/qiskit-aer#1761 is in a release version of Aer. - "Setting metadata to None.*", - # and this one once Qiskit/qiskit-aer#1945 is merged and released. - r"The method ``qiskit\.circuit\.quantumcircuit\.QuantumCircuit\.i\(\)`` is " - r"deprecated as of qiskit 0\.45\.0\. It will be removed no earlier than 3 " - r"months after the release date\. Use QuantumCircuit\.id as direct replacement\.", - ] - - for msg in allow_aer_DeprecationWarning_message: - warnings.filterwarnings( - "default", category=DeprecationWarning, module="qiskit_aer.*", message=msg - ) - class FullQiskitTestCase(QiskitTestCase): """Terra-specific further additions for test cases, if ``testtools`` is available. diff --git a/qiskit/test/decorators.py b/qiskit/test/decorators.py index a15ab9ccc361..c4ca9c67459f 100644 --- a/qiskit/test/decorators.py +++ b/qiskit/test/decorators.py @@ -15,7 +15,6 @@ import collections.abc import functools -import os import socket import sys from typing import Union, Callable, Type, Iterable @@ -99,91 +98,6 @@ def _wrapper(*args, **kwargs): return _wrapper -def _get_credentials(): - """Finds the credentials for a specific test and options. - - Returns: - Credentials: set of credentials - - Raises: - SkipTest: when credentials can't be found - """ - try: - from qiskit.providers.ibmq.credentials import Credentials, discover_credentials - except ImportError as ex: - raise unittest.SkipTest( - "qiskit-ibmq-provider could not be found, " - "and is required for executing online tests. " - 'To install, run "pip install qiskit-ibmq-provider" ' - "or check your installation." - ) from ex - - if os.getenv("IBMQ_TOKEN") and os.getenv("IBMQ_URL"): - return Credentials(os.getenv("IBMQ_TOKEN"), os.getenv("IBMQ_URL")) - elif os.getenv("QISKIT_TESTS_USE_CREDENTIALS_FILE"): - # Attempt to read the standard credentials. - discovered_credentials = discover_credentials() - - if discovered_credentials: - # Decide which credentials to use for testing. - if len(discovered_credentials) > 1: - raise unittest.SkipTest( - "More than 1 credential set found, use: " - "IBMQ_TOKEN and IBMQ_URL env variables to " - "set credentials explicitly" - ) - - # Use the first available credentials. - return list(discovered_credentials.values())[0] - raise unittest.SkipTest( - "No IBMQ credentials found for running the test. This is required for running online tests." - ) - - -def online_test(func): - """Decorator that signals that the test uses the network (and the online API): - - It involves: - * determines if the test should be skipped by checking environment - variables. - * if the `USE_ALTERNATE_ENV_CREDENTIALS` environment variable is - set, it reads the credentials from an alternative set of environment - variables. - * if the test is not skipped, it reads `qe_token` and `qe_url` from - `Qconfig.py`, environment variables or qiskitrc. - * if the test is not skipped, it appends `qe_token` and `qe_url` as - arguments to the test function. - - Args: - func (callable): test function to be decorated. - - Returns: - callable: the decorated function. - """ - - @functools.wraps(func) - def _wrapper(self, *args, **kwargs): - # To avoid checking the connection in each test - global HAS_NET_CONNECTION # pylint: disable=global-statement - - if TEST_OPTIONS["skip_online"]: - raise unittest.SkipTest("Skipping online tests") - - if HAS_NET_CONNECTION is None: - HAS_NET_CONNECTION = _has_connection("qiskit.org", 443) - - if not HAS_NET_CONNECTION: - raise unittest.SkipTest("Test requires internet connection.") - - credentials = _get_credentials() - self.using_ibmq_credentials = credentials.is_ibmq() - kwargs.update({"qe_token": credentials.token, "qe_url": credentials.url}) - - return func(self, *args, **kwargs) - - return _wrapper - - def enforce_subclasses_call( methods: Union[str, Iterable[str]], attr: str = "_enforce_subclasses_call_cache" ) -> Callable[[Type], Type]: diff --git a/qiskit/test/ibmq_mock.py b/qiskit/test/ibmq_mock.py deleted file mode 100644 index 73b30f4f44ec..000000000000 --- a/qiskit/test/ibmq_mock.py +++ /dev/null @@ -1,45 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Mock functions for qiskit.IBMQ.""" - -from unittest.mock import MagicMock -import qiskit -from qiskit.providers import fake_provider as backend_mocks - - -def mock_get_backend(backend): - """Replace qiskit.IBMQ with a mock that returns a single backend. - - Note this will set the value of qiskit.IBMQ to a MagicMock object. It is - intended to be run as part of docstrings with jupyter-example in a hidden - cell so that later examples which rely on ibmq devices so that the docs can - be built without requiring configured credentials. If used outside of this - context be aware that you will have to manually restore qiskit.IBMQ the - value to qiskit.providers.ibmq.IBMQ after you finish using your mock. - - Args: - backend (str): The class name as a string for the fake device to - return from the mock IBMQ object. For example, FakeVigo. - Raises: - NameError: If the specified value of backend - """ - mock_ibmq = MagicMock() - mock_provider = MagicMock() - if not hasattr(backend_mocks, backend): - raise NameError( - "The specified backend name is not a valid mock from qiskit.providers.fake_provider." - ) - fake_backend = getattr(backend_mocks, backend)() - mock_provider.get_backend.return_value = fake_backend - mock_ibmq.get_provider.return_value = mock_provider - qiskit.IBMQ = mock_ibmq diff --git a/qiskit/test/mock/__init__.py b/qiskit/test/mock/__init__.py deleted file mode 100644 index 760a0b1772a9..000000000000 --- a/qiskit/test/mock/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Utilities for mocking the IBMQ provider, including job responses and backends. - -The module includes dummy provider, backends, and jobs. -The purpose of these classes is to fake backends for testing purposes: -testing local timeouts, arbitrary responses or behavior, etc. - -The mock devices are mainly for testing the compiler. -""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import the desired object directly 'qiskit.providers.fake_provider'.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/__init__.py b/qiskit/test/mock/backends/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/almaden/__init__.py b/qiskit/test/mock/backends/almaden/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/almaden/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/armonk/__init__.py b/qiskit/test/mock/backends/armonk/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/armonk/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/athens/__init__.py b/qiskit/test/mock/backends/athens/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/athens/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/belem/__init__.py b/qiskit/test/mock/backends/belem/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/belem/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/boeblingen/__init__.py b/qiskit/test/mock/backends/boeblingen/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/boeblingen/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/bogota/__init__.py b/qiskit/test/mock/backends/bogota/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/bogota/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/brooklyn/__init__.py b/qiskit/test/mock/backends/brooklyn/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/brooklyn/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/burlington/__init__.py b/qiskit/test/mock/backends/burlington/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/burlington/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/cairo/__init__.py b/qiskit/test/mock/backends/cairo/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/cairo/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/cambridge/__init__.py b/qiskit/test/mock/backends/cambridge/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/cambridge/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/casablanca/__init__.py b/qiskit/test/mock/backends/casablanca/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/casablanca/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/essex/__init__.py b/qiskit/test/mock/backends/essex/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/essex/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/guadalupe/__init__.py b/qiskit/test/mock/backends/guadalupe/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/guadalupe/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/hanoi/__init__.py b/qiskit/test/mock/backends/hanoi/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/hanoi/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/jakarta/__init__.py b/qiskit/test/mock/backends/jakarta/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/jakarta/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/johannesburg/__init__.py b/qiskit/test/mock/backends/johannesburg/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/johannesburg/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/kolkata/__init__.py b/qiskit/test/mock/backends/kolkata/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/kolkata/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/lagos/__init__.py b/qiskit/test/mock/backends/lagos/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/lagos/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/lima/__init__.py b/qiskit/test/mock/backends/lima/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/lima/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/london/__init__.py b/qiskit/test/mock/backends/london/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/london/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/manhattan/__init__.py b/qiskit/test/mock/backends/manhattan/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/manhattan/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/manila/__init__.py b/qiskit/test/mock/backends/manila/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/manila/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/melbourne/__init__.py b/qiskit/test/mock/backends/melbourne/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/melbourne/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/montreal/__init__.py b/qiskit/test/mock/backends/montreal/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/montreal/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/mumbai/__init__.py b/qiskit/test/mock/backends/mumbai/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/mumbai/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/nairobi/__init__.py b/qiskit/test/mock/backends/nairobi/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/nairobi/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/ourense/__init__.py b/qiskit/test/mock/backends/ourense/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/ourense/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/paris/__init__.py b/qiskit/test/mock/backends/paris/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/paris/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/poughkeepsie/__init__.py b/qiskit/test/mock/backends/poughkeepsie/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/poughkeepsie/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/quito/__init__.py b/qiskit/test/mock/backends/quito/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/quito/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/rochester/__init__.py b/qiskit/test/mock/backends/rochester/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/rochester/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/rome/__init__.py b/qiskit/test/mock/backends/rome/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/rome/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/rueschlikon/__init__.py b/qiskit/test/mock/backends/rueschlikon/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/rueschlikon/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/santiago/__init__.py b/qiskit/test/mock/backends/santiago/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/santiago/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/singapore/__init__.py b/qiskit/test/mock/backends/singapore/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/singapore/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/sydney/__init__.py b/qiskit/test/mock/backends/sydney/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/sydney/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/tenerife/__init__.py b/qiskit/test/mock/backends/tenerife/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/tenerife/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/tokyo/__init__.py b/qiskit/test/mock/backends/tokyo/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/tokyo/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/toronto/__init__.py b/qiskit/test/mock/backends/toronto/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/toronto/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/valencia/__init__.py b/qiskit/test/mock/backends/valencia/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/valencia/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/vigo/__init__.py b/qiskit/test/mock/backends/vigo/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/vigo/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/washington/__init__.py b/qiskit/test/mock/backends/washington/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/washington/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/test/mock/backends/yorktown/__init__.py b/qiskit/test/mock/backends/yorktown/__init__.py deleted file mode 100644 index 7682c117d41c..000000000000 --- a/qiskit/test/mock/backends/yorktown/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Deprecation warnings for moved functionality.""" - -import warnings - -import qiskit.providers.fake_provider - - -def __getattr__(name): - if name.startswith("_"): - # Some Python components (including tests) do funny things with dunders. - raise AttributeError(f"module '{__name__}' has no attribute '{name}'") - - warnings.warn( - f"The module '{__name__}' is deprecated since " - "Qiskit Terra 0.21.0, and will be removed 3 months or more later. " - "Instead, you should import from `qiskit.providers.fake_provider` directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return getattr(qiskit.providers.fake_provider, name) diff --git a/qiskit/tools/__init__.py b/qiskit/tools/__init__.py index 6bddd22ba889..38354279117a 100644 --- a/qiskit/tools/__init__.py +++ b/qiskit/tools/__init__.py @@ -29,16 +29,9 @@ Monitoring ---------- -A helper module to get IBM backend information and submitted job status. - -.. autofunction:: job_monitor -.. autofunction:: backend_monitor -.. autofunction:: backend_overview - .. automodule:: qiskit.tools.events """ from .parallel import parallel_map -from .monitor import job_monitor, backend_monitor, backend_overview from .events import progressbar diff --git a/qiskit/tools/jupyter/__init__.py b/qiskit/tools/jupyter/__init__.py deleted file mode 100644 index 6bc05d46f743..000000000000 --- a/qiskit/tools/jupyter/__init__.py +++ /dev/null @@ -1,138 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -=========================================== -Jupyter Tools (:mod:`qiskit.tools.jupyter`) -=========================================== - -.. currentmodule:: qiskit.tools.jupyter - -A Collection of Jupyter magic functions and tools -that extend the functionality of Qiskit. - -Overview of all available backends -================================== - -.. code-block:: - - from qiskit.providers.ibmq import IBMQ - import qiskit.tools.jupyter - %matplotlib inline - - IBMQ.load_account() - - %qiskit_backend_overview - - -Detailed information on a single backend -======================================== - -.. code-block:: - - from qiskit.providers.ibmq import IBMQ - import qiskit.tools.jupyter - %matplotlib inline - - IBMQ.load_account() - provider = IBMQ.get_provider(hub='ibm-q') - backend = provider.get_backend('ibmq_vigo') - backend - - -Load Qiskit Job Watcher -======================= - -.. code-block:: - - import qiskit.tools.jupyter - %qiskit_job_watcher - - -HTMLProgressBar -=============== - -.. code-block:: - - import numpy as np - from qiskit.tools.parallel import parallel_map - import qiskit.tools.jupyter - - %qiskit_progress_bar - parallel_map(np.sin, np.linspace(0,10,100)); - - -Qiskit version table -==================== - -.. code-block:: - - import qiskit.tools.jupyter - %qiskit_version_table - - -Qiskit copyright -================ - -.. code-block:: - - import qiskit.tools.jupyter - %qiskit_copyright - -Monospaced output -================= - -.. code-block:: - - import qiskit.tools.jupyter - %monospaced_output - -""" -import warnings - -from IPython import get_ipython -from qiskit.providers.fake_provider import FakeBackend -from qiskit.utils import optionals as _optionals -from .jupyter_magics import ProgressBarMagic, StatusMagic -from .progressbar import HTMLProgressBar -from .version_table import VersionTable -from .copyright import Copyright -from .monospace import MonospacedOutput -from .job_watcher import JobWatcher, JobWatcherMagic - -_IP = get_ipython() -if _IP is not None: - _IP.register_magics(ProgressBarMagic) - _IP.register_magics(VersionTable) - _IP.register_magics(MonospacedOutput) - _IP.register_magics(Copyright) - _IP.register_magics(JobWatcherMagic) - if _optionals.HAS_MATPLOTLIB: - from .backend_overview import BackendOverview - from .backend_monitor import _backend_monitor - - _IP.register_magics(BackendOverview) - if _optionals.HAS_IBMQ: - from qiskit.providers.ibmq import IBMQBackend # pylint: disable=no-name-in-module - - HTML_FORMATTER = _IP.display_formatter.formatters["text/html"] - # Make _backend_monitor the html repr for IBM Q backends - HTML_FORMATTER.for_type(IBMQBackend, _backend_monitor) - HTML_FORMATTER.for_type(FakeBackend, _backend_monitor) - else: - warnings.warn( - "matplotlib can't be found, ensure you have matplotlib and other " - "visualization dependencies installed. You can run " - "'!pip install qiskit-terra[visualization]' to install it from " - "jupyter", - RuntimeWarning, - ) diff --git a/qiskit/tools/jupyter/backend_monitor.py b/qiskit/tools/jupyter/backend_monitor.py deleted file mode 100644 index ecb401521caf..000000000000 --- a/qiskit/tools/jupyter/backend_monitor.py +++ /dev/null @@ -1,588 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A module for monitoring backends.""" - -import types -import math -import datetime -from IPython.display import display -import matplotlib.pyplot as plt -from matplotlib.patches import Circle -import ipywidgets as widgets -from qiskit.exceptions import QiskitError -from qiskit.visualization.gate_map import plot_gate_map, plot_error_map -from qiskit.providers.fake_provider import FakeBackend - -try: - from qiskit.providers.ibmq import IBMQBackend -except ImportError: - pass - -MONTH_NAMES = { - 1: "Jan.", - 2: "Feb.", - 3: "Mar.", - 4: "Apr.", - 5: "May", - 6: "June", - 7: "July", - 8: "Aug.", - 9: "Sept.", - 10: "Oct.", - 11: "Nov.", - 12: "Dec.", -} - - -def _load_jobs_data(self, change): - """Loads backend jobs data""" - if change["new"] == 4 and not self._did_jobs: - self._did_jobs = True - year = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - month = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - week = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - self.children[4].children = [year, month, week] - self.children[4].set_title(0, "Year") - self.children[4].set_title(1, "Month") - self.children[4].set_title(2, "Week") - self.children[4].selected_index = 1 - _build_job_history(self.children[4], self._backend) - - -def _backend_monitor(backend): - """A private function to generate a monitor widget - for a IBMQ backend repr. - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Raises: - QiskitError: Input is not an IBMQBackend - """ - if not isinstance(backend, IBMQBackend) and not isinstance(backend, FakeBackend): - raise QiskitError("Input variable is not of type IBMQBackend.") - title_style = "style='color:#ffffff;background-color:#000000;padding-top: 1%;" - title_style += "padding-bottom: 1%;padding-left: 1%; margin-top: 0px'" - title_html = f"

{backend.name()}

" - - details = [config_tab(backend)] - - tab_contents = ["Configuration"] - - # Empty jobs tab widget - jobs = widgets.Tab(layout=widgets.Layout(max_height="620px")) - - if not backend.configuration().simulator: - tab_contents.extend(["Qubit Properties", "Multi-Qubit Gates", "Error Map", "Job History"]) - - details.extend([qubits_tab(backend), gates_tab(backend), detailed_map(backend), jobs]) - - tabs = widgets.Tab(layout=widgets.Layout(overflow_y="scroll")) - tabs.children = details - for i in range(len(details)): - tabs.set_title(i, tab_contents[i]) - - # Make backend accessible to tabs widget - tabs._backend = backend - tabs._did_jobs = False - tabs._update = types.MethodType(_load_jobs_data, tabs) - - tabs.observe(tabs._update, names="selected_index") - - title_widget = widgets.HTML(value=title_html, layout=widgets.Layout(margin="0px 0px 0px 0px")) - - bmonitor = widgets.VBox( - [title_widget, tabs], - layout=widgets.Layout( - border="4px solid #000000", max_height="650px", min_height="650px", overflow_y="hidden" - ), - ) - display(bmonitor) - - -def config_tab(backend): - """The backend configuration widget. - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - grid: A GridBox widget. - """ - status = backend.status().to_dict() - config = backend.configuration().to_dict() - - config_dict = {**status, **config} - - upper_list = ["n_qubits"] - - if "quantum_volume" in config.keys(): - if config["quantum_volume"]: - upper_list.append("quantum_volume") - - upper_list.extend( - [ - "operational", - "status_msg", - "pending_jobs", - "backend_version", - "basis_gates", - "max_shots", - "max_experiments", - ] - ) - - lower_list = list(set(config_dict.keys()).difference(upper_list)) - # Remove gates because they are in a different tab - lower_list.remove("gates") - # Look for hamiltonian - if "hamiltonian" in lower_list: - htex = config_dict["hamiltonian"]["h_latex"] - config_dict["hamiltonian"] = "$$%s$$" % htex - - upper_str = "" - upper_str += """""" - - footer = "
" - - # Upper HBox widget data - - upper_str += "PropertyValue" - for key in upper_list: - upper_str += "{}{}".format( - key, - config_dict[key], - ) - upper_str += footer - - upper_table = widgets.HTMLMath( - value=upper_str, layout=widgets.Layout(width="100%", grid_area="left") - ) - - image_widget = widgets.Output( - layout=widgets.Layout( - display="flex-inline", - grid_area="right", - padding="10px 10px 10px 10px", - width="auto", - max_height="325px", - align_items="center", - ) - ) - - if not config["simulator"]: - with image_widget: - qubit_size = 24 - if config["n_qubits"] > 20: - qubit_size = 34 - gate_map = plot_gate_map(backend, qubit_size=qubit_size) - display(gate_map) - plt.close(gate_map) - - lower_str = "" - lower_str += """""" - lower_str += "" - for key in lower_list: - if key != "name": - lower_str += f"" - lower_str += footer - - lower_table = widgets.HTMLMath( - value=lower_str, layout=widgets.Layout(width="auto", grid_area="bottom") - ) - - grid = widgets.GridBox( - children=[upper_table, image_widget, lower_table], - layout=widgets.Layout( - grid_template_rows="auto auto", - grid_template_columns="31% 23% 23% 23%", - grid_template_areas=""" - "left right right right" - "bottom bottom bottom bottom" - """, - grid_gap="0px 0px", - ), - ) - - return grid - - -def qubits_tab(backend): - """The qubits properties widget - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - VBox: A VBox widget. - """ - props = backend.properties() - - header_html = "
{key}: {value}
" - update_date = props.last_update_date.strftime("%a %d %B %Y at %H:%M %Z") - header_html = header_html.format(key="last_update_date", value=update_date) - - update_date_widget = widgets.HTML(value=header_html) - - qubit_html = "
{key}{config_dict[key]}
" - qubit_html += """""" - - qubit_html += "" - qubit_footer = "
FrequencyT1T2
" - - gate_error_title = "" - - for index, qubit_data in enumerate(props.qubits): - name = "Q%s" % index - gate_data = [gate for gate in props.gates if gate.qubits == [index]] - - cal_data = dict.fromkeys(["T1", "T2", "frequency", "readout_error"], "Unknown") - for nduv in qubit_data: - if nduv.name in cal_data: - cal_data[nduv.name] = str(round(nduv.value, 5)) + " " + nduv.unit - - gate_names = [] - gate_error = [] - for gd in gate_data: - if gd.gate in ["id"]: - continue - try: - gate_error.append(str(round(props.gate_error(gd.gate, index), 5))) - gate_names.append(gd.gate.upper()) - except QiskitError: - pass - - if not gate_error_title: - for gname in gate_names: - gate_error_title += f"{gname}" - qubit_html += gate_error_title + "Readout error" - - qubit_html += f"{name}" - qubit_html += ( - f"{cal_data['frequency']}{cal_data['T1']}{cal_data['T2']}" - ) - for gerror in gate_error: - qubit_html += f"{gerror}" - qubit_html += f"{cal_data['readout_error']}" - - qubit_html += qubit_footer - - qubit_widget = widgets.HTML(value=qubit_html) - - out = widgets.VBox([update_date_widget, qubit_widget]) - - return out - - -def gates_tab(backend): - """The multiple qubit gate error widget. - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - VBox: A VBox widget. - """ - props = backend.properties() - - multi_qubit_gates = [g for g in props.gates if len(g.qubits) > 1] - - header_html = "
{key}: {value}
" - header_html = header_html.format(key="last_update_date", value=props.last_update_date) - - update_date_widget = widgets.HTML(value=header_html, layout=widgets.Layout(grid_area="top")) - - gate_html = "" - gate_html += """""" - - gate_html += "" - gate_footer = "
TypeGate error
" - - # Split gates into two columns - left_num = math.ceil(len(multi_qubit_gates) / 3) - mid_num = math.ceil((len(multi_qubit_gates) - left_num) / 2) - - left_table = gate_html - - for qub in range(left_num): - gate = multi_qubit_gates[qub] - qubits = gate.qubits - ttype = gate.gate - error = round(props.gate_error(gate.gate, qubits), 5) - - left_table += "%s" - left_table += "%s%s" - left_table = left_table % (f"{ttype}{qubits[0]}_{qubits[1]}", ttype, error) - left_table += gate_footer - - middle_table = gate_html - - for qub in range(left_num, left_num + mid_num): - gate = multi_qubit_gates[qub] - qubits = gate.qubits - ttype = gate.gate - error = round(props.gate_error(gate.gate, qubits), 5) - - middle_table += "%s" - middle_table += "%s%s" - middle_table = middle_table % (f"{ttype}{qubits[0]}_{qubits[1]}", ttype, error) - middle_table += gate_footer - - right_table = gate_html - - for qub in range(left_num + mid_num, len(multi_qubit_gates)): - gate = multi_qubit_gates[qub] - qubits = gate.qubits - ttype = gate.gate - error = round(props.gate_error(gate.gate, qubits), 5) - - right_table += "%s" - right_table += "%s%s" - right_table = right_table % (f"{ttype}{qubits[0]}_{qubits[1]}", ttype, error) - right_table += gate_footer - - left_table_widget = widgets.HTML(value=left_table, layout=widgets.Layout(grid_area="left")) - middle_table_widget = widgets.HTML( - value=middle_table, layout=widgets.Layout(grid_area="middle") - ) - right_table_widget = widgets.HTML(value=right_table, layout=widgets.Layout(grid_area="right")) - - grid = widgets.GridBox( - children=[update_date_widget, left_table_widget, middle_table_widget, right_table_widget], - layout=widgets.Layout( - grid_template_rows="auto auto", - grid_template_columns="33% 33% 33%", - grid_template_areas=""" - "top top top" - "left middle right" - """, - grid_gap="0px 0px", - ), - ) - - return grid - - -def detailed_map(backend): - """Widget for displaying detailed noise map. - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - GridBox: Widget holding noise map images. - """ - error_widget = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center") - ) - with error_widget: - display(plot_error_map(backend, figsize=(11, 9), show_title=False)) - return error_widget - - -def job_history(backend): - """Widget for displaying job history - - Args: - backend (IBMQBackend | FakeBackend): The backend. - - Returns: - Tab: A tab widget for history images. - """ - year = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - month = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - week = widgets.Output( - layout=widgets.Layout(display="flex-inline", align_items="center", min_height="400px") - ) - - tabs = widgets.Tab(layout=widgets.Layout(max_height="620px")) - tabs.children = [year, month, week] - tabs.set_title(0, "Year") - tabs.set_title(1, "Month") - tabs.set_title(2, "Week") - tabs.selected_index = 1 - - _build_job_history(tabs, backend) - return tabs - - -def _build_job_history(tabs, backend): - - past_year_date = datetime.datetime.now() - datetime.timedelta(days=365) - date_filter = {"creationDate": {"gt": past_year_date.isoformat()}} - jobs = backend.jobs(limit=None, db_filter=date_filter) - - with tabs.children[0]: - year_plot = plot_job_history(jobs, interval="year") - display(year_plot) - plt.close(year_plot) - - with tabs.children[1]: - month_plot = plot_job_history(jobs, interval="month") - display(month_plot) - plt.close(month_plot) - - with tabs.children[2]: - week_plot = plot_job_history(jobs, interval="week") - display(week_plot) - plt.close(week_plot) - - -def plot_job_history(jobs, interval="year"): - """Plots the job history of the user from the given list of jobs. - - Args: - jobs (list): A list of jobs with type IBMQjob. - interval (str): Interval over which to examine. - - Returns: - fig: A Matplotlib figure instance. - """ - - def get_date(job): - """Returns a datetime object from a IBMQJob instance. - - Args: - job (IBMQJob): A job. - - Returns: - dt: A datetime object. - """ - creation_date = job.creation_date() - - if isinstance(creation_date, datetime.datetime): - return creation_date - - return datetime.datetime.strptime(creation_date, "%Y-%m-%dT%H:%M:%S.%fZ") - - current_time = datetime.datetime.now() - - if interval == "year": - bins = [(current_time - datetime.timedelta(days=k * 365 / 12)) for k in range(12)] - elif interval == "month": - bins = [(current_time - datetime.timedelta(days=k)) for k in range(30)] - elif interval == "week": - bins = [(current_time - datetime.timedelta(days=k)) for k in range(7)] - - binned_jobs = [0] * len(bins) - - if interval == "year": - for job in jobs: - for ind, dat in enumerate(bins): - date = get_date(job) - if date.month == dat.month: - binned_jobs[ind] += 1 - break - else: - continue - else: - for job in jobs: - for ind, dat in enumerate(bins): - date = get_date(job) - if date.day == dat.day and date.month == dat.month: - binned_jobs[ind] += 1 - break - else: - continue - - nz_bins = [] - nz_idx = [] - for ind, val in enumerate(binned_jobs): - if val != 0: - nz_idx.append(ind) - nz_bins.append(val) - - total_jobs = sum(binned_jobs) - - colors = ["#003f5c", "#ffa600", "#374c80", "#ff764a", "#7a5195", "#ef5675", "#bc5090"] - - if interval == "year": - labels = [f"{str(bins[b].year)[2:]}-{MONTH_NAMES[bins[b].month]}" for b in nz_idx] - else: - labels = [f"{MONTH_NAMES[bins[b].month]}-{bins[b].day}" for b in nz_idx] - fig, ax = plt.subplots(1, 1, figsize=(5.5, 5.5)) - ax.pie( - nz_bins[::-1], - labels=labels, - colors=colors, - textprops={"fontsize": 14}, - rotatelabels=True, - counterclock=False, - radius=1, - ) - ax.add_artist(Circle((0, 0), 0.7, color="white", zorder=1)) - ax.text(0, 0, total_jobs, horizontalalignment="center", verticalalignment="center", fontsize=26) - return fig diff --git a/qiskit/tools/jupyter/backend_overview.py b/qiskit/tools/jupyter/backend_overview.py deleted file mode 100644 index f72ad8ba1938..000000000000 --- a/qiskit/tools/jupyter/backend_overview.py +++ /dev/null @@ -1,322 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A module for monitoring backends.""" - -import time -import threading -import types -from IPython.display import display -from IPython.core.magic import line_magic, Magics, magics_class -from IPython.core import magic_arguments -import matplotlib.pyplot as plt -import ipywidgets as widgets -from qiskit.tools.monitor.overview import get_unique_backends -from qiskit.visualization.gate_map import plot_gate_map - - -@magics_class -class BackendOverview(Magics): - """A class of status magic functions.""" - - @line_magic - @magic_arguments.magic_arguments() - @magic_arguments.argument( - "-i", "--interval", type=float, default=60, help="Interval for status check." - ) - def qiskit_backend_overview(self, line=""): - """A Jupyter magic function to monitor backends.""" - args = magic_arguments.parse_argstring(self.qiskit_backend_overview, line) - - unique_hardware_backends = get_unique_backends() - _value = "

Backend Overview

" - backend_title = widgets.HTML(value=_value, layout=widgets.Layout(margin="0px 0px 0px 0px")) - - build_back_widgets = [backend_widget(b) for b in unique_hardware_backends] - - _backends = [] - # Sort backends by operational or not - oper_ord_backends = [] - for n, back in enumerate(unique_hardware_backends): - if back.status().operational: - oper_ord_backends = [build_back_widgets[n]] + oper_ord_backends - _backends = [back] + _backends - else: - oper_ord_backends = oper_ord_backends + [build_back_widgets[n]] - _backends = _backends + [back] - - qubit_label = widgets.Label(value="Num. Qubits") - qv_label = widgets.Label(value="Quantum Vol.") - pend_label = widgets.Label( - value="Pending Jobs", layout=widgets.Layout(margin="5px 0px 0px 0px") - ) - least_label = widgets.Label( - value="Least Busy", layout=widgets.Layout(margin="10px 0px 0px 0px") - ) - oper_label = widgets.Label( - value="Operational", layout=widgets.Layout(margin="5px 0px 0px 0px") - ) - t12_label = widgets.Label( - value="Avg. T1 / T2", layout=widgets.Layout(margin="10px 0px 0px 0px") - ) - cx_label = widgets.Label( - value="Avg. CX Err.", layout=widgets.Layout(margin="8px 0px 0px 0px") - ) - meas_label = widgets.Label( - value="Avg. Meas. Err.", layout=widgets.Layout(margin="8px 0px 0px 0px") - ) - - labels_widget = widgets.VBox( - [ - qubit_label, - qv_label, - pend_label, - oper_label, - least_label, - t12_label, - cx_label, - meas_label, - ], - layout=widgets.Layout(margin="295px 0px 0px 0px", min_width="100px"), - ) - - backend_grid = GridBox_with_thread( - children=oper_ord_backends, - layout=widgets.Layout( - grid_template_columns="250px " * len(unique_hardware_backends), - grid_template_rows="auto", - grid_gap="0px 25px", - ), - ) - - backend_grid._backends = _backends # pylint: disable=attribute-defined-outside-init - backend_grid._update = types.MethodType( # pylint: disable=attribute-defined-outside-init - update_backend_info, backend_grid - ) - - backend_grid._thread = threading.Thread( # pylint: disable=attribute-defined-outside-init - target=backend_grid._update, args=(args.interval,) - ) - backend_grid._thread.start() - - back_box = widgets.HBox([labels_widget, backend_grid]) - - back_monitor = widgets.VBox([backend_title, back_box]) - display(back_monitor) - - -class GridBox_with_thread(widgets.GridBox): # pylint: disable=invalid-name - """A GridBox that will close an attached thread""" - - def __del__(self): - """Object disposal""" - if hasattr(self, "_thread"): - try: - self._thread.do_run = False - self._thread.join() - except Exception: # pylint: disable=broad-except - pass - self.close() - - -def backend_widget(backend): - """Creates a backend widget.""" - config = backend.configuration().to_dict() - props = backend.properties().to_dict() - - name = widgets.HTML(value=f"

{backend.name()}

", layout=widgets.Layout()) - - num_qubits = config["n_qubits"] - - qv_val = "-" - if "quantum_volume" in config.keys(): - if config["quantum_volume"]: - qv_val = config["quantum_volume"] - - qubit_count = widgets.HTML( - value=f"
{num_qubits}
", - layout=widgets.Layout(justify_content="center"), - ) - - qv_value = widgets.HTML( - value=f"
{qv_val}
", - layout=widgets.Layout(justify_content="center"), - ) - - cmap = widgets.Output( - layout=widgets.Layout( - min_width="250px", - max_width="250px", - max_height="250px", - min_height="250px", - justify_content="center", - align_items="center", - margin="0px 0px 0px 0px", - ) - ) - - with cmap: - _cmap_fig = plot_gate_map(backend, plot_directed=False, label_qubits=False) - if _cmap_fig is not None: - display(_cmap_fig) - # Prevents plot from showing up twice. - plt.close(_cmap_fig) - - pending = generate_jobs_pending_widget() - - is_oper = widgets.HTML(value="
", layout=widgets.Layout(justify_content="center")) - - least_busy = widgets.HTML(value="
", layout=widgets.Layout(justify_content="center")) - - t1_units = props["qubits"][0][0]["unit"] - avg_t1 = round(sum(q[0]["value"] for q in props["qubits"]) / num_qubits, 1) - avg_t2 = round(sum(q[1]["value"] for q in props["qubits"]) / num_qubits, 1) - t12_widget = widgets.HTML( - value=f"
{avg_t1} / {avg_t2} {t1_units}
", - layout=widgets.Layout(), - ) - - avg_cx_err = "NA" - if config["coupling_map"]: - sum_cx_err = 0 - num_cx = 0 - for gate in props["gates"]: - if gate["gate"] == "cx": - for param in gate["parameters"]: - if param["name"] == "gate_error": - # Value == 1.0 means gate effectively off - if param["value"] != 1.0: - sum_cx_err += param["value"] - num_cx += 1 - if num_cx > 0: - avg_cx_err = round(sum_cx_err / num_cx, 4) - - cx_widget = widgets.HTML(value=f"
{avg_cx_err}
", layout=widgets.Layout()) - - avg_meas_err = 0 - for qub in props["qubits"]: - for item in qub: - if item["name"] == "readout_error": - avg_meas_err += item["value"] - avg_meas_err = round(avg_meas_err / num_qubits, 4) - meas_widget = widgets.HTML(value=f"
{avg_meas_err}
", layout=widgets.Layout()) - - out = widgets.VBox( - [ - name, - cmap, - qubit_count, - qv_value, - pending, - is_oper, - least_busy, - t12_widget, - cx_widget, - meas_widget, - ], - layout=widgets.Layout(display="inline-flex", flex_flow="column", align_items="center"), - ) - - out._is_alive = True - return out - - -def update_backend_info(self, interval=60): - """Updates the monitor info - Called from another thread. - """ - my_thread = threading.current_thread() - current_interval = 0 - started = False - all_dead = False - stati = [None] * len(self._backends) - while getattr(my_thread, "do_run", True) and not all_dead: - if current_interval == interval or started is False: - for ind, back in enumerate(self._backends): - _value = self.children[ind].children[2].value - _head = _value.split("")[0] - try: - _status = back.status() - stati[ind] = _status - except Exception: # pylint: disable=broad-except - self.children[ind].children[2].value = _value.replace( - _head, "
" - ) - self.children[ind]._is_alive = False - else: - self.children[ind]._is_alive = True - self.children[ind].children[2].value = _value.replace(_head, "
") - - idx = list(range(len(self._backends))) - pending = [s.pending_jobs for s in stati] - _, least_idx = zip(*sorted(zip(pending, idx))) - - # Make sure least pending is operational - for ind in least_idx: - if stati[ind].operational: - least_pending_idx = ind - break - - for var in idx: - if var == least_pending_idx: - self.children[var].children[6].value = "
True
" - else: - self.children[var].children[6].value = "
False
" - - self.children[var].children[4].children[1].max = max( - self.children[var].children[4].children[1].max, pending[var] + 10 - ) - self.children[var].children[4].children[1].value = pending[var] - if stati[var].operational: - self.children[var].children[5].value = "
True
" - else: - self.children[var].children[5].value = "
False
" - - started = True - current_interval = 0 - time.sleep(1) - all_dead = not any(wid._is_alive for wid in self.children) - current_interval += 1 - - -def generate_jobs_pending_widget(): - """Generates a jobs_pending progress bar widget.""" - pbar = widgets.IntProgress( - value=0, - min=0, - max=50, - description="", - orientation="horizontal", - layout=widgets.Layout(max_width="180px"), - ) - pbar.style.bar_color = "#71cddd" - - pbar_current = widgets.Label(value=str(pbar.value), layout=widgets.Layout(min_width="auto")) - pbar_max = widgets.Label(value=str(pbar.max), layout=widgets.Layout(min_width="auto")) - - def _on_max_change(change): - pbar_max.value = str(change["new"]) - - def _on_val_change(change): - pbar_current.value = str(change["new"]) - - pbar.observe(_on_max_change, names="max") - pbar.observe(_on_val_change, names="value") - - jobs_widget = widgets.HBox( - [pbar_current, pbar, pbar_max], - layout=widgets.Layout(max_width="250px", min_width="250px", justify_content="center"), - ) - - return jobs_widget diff --git a/qiskit/tools/jupyter/copyright.py b/qiskit/tools/jupyter/copyright.py deleted file mode 100644 index d759a6ab5f4f..000000000000 --- a/qiskit/tools/jupyter/copyright.py +++ /dev/null @@ -1,42 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# pylint: disable=unused-argument - -"""A module for monitoring backends.""" - -import datetime -from IPython.display import HTML, display -from IPython.core.magic import line_magic, Magics, magics_class - - -@magics_class -class Copyright(Magics): - """A class of status magic functions.""" - - @line_magic - def qiskit_copyright(self, line="", cell=None): - """A Jupyter magic function return qiskit copyright""" - now = datetime.datetime.now() - - html = "
" - html += "

This code is a part of Qiskit

" - html += "

© Copyright IBM 2017, %s.

" % now.year - html += "

This code is licensed under the Apache License, Version 2.0. You may
" - html += "obtain a copy of this license in the LICENSE.txt file in the root directory
" - html += "of this source tree or at http://www.apache.org/licenses/LICENSE-2.0." - - html += "

Any modifications or derivative works of this code must retain this
" - html += "copyright notice, and modified files need to carry a notice indicating
" - html += "that they have been altered from the originals.

" - html += "
" - return display(HTML(html)) diff --git a/qiskit/tools/jupyter/job_watcher.py b/qiskit/tools/jupyter/job_watcher.py deleted file mode 100644 index 7ad6f7014569..000000000000 --- a/qiskit/tools/jupyter/job_watcher.py +++ /dev/null @@ -1,167 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# pylint: disable=unused-argument - -"""A module for the job watcher""" - -from IPython.core.magic import line_magic, Magics, magics_class -from qiskit.tools.events.pubsub import Subscriber -from qiskit.utils import optionals as _optionals - -from .job_widgets import build_job_viewer, make_clear_button, make_labels, create_job_widget -from .watcher_monitor import _job_monitor - - -@_optionals.HAS_IBMQ.require_in_instance -class JobWatcher(Subscriber): - """An IBM Q job watcher.""" - - def __init__(self): - super().__init__() - self.jobs = [] - self._init_subscriber() - self.job_viewer = None - self._clear_button = make_clear_button(self) - self._labels = make_labels() - self.refresh_viewer() - - def refresh_viewer(self): - """Refreshes the job viewer.""" - if self.job_viewer is not None: - self.job_viewer.children[0].children = [self._clear_button, self._labels] + list( - reversed(self.jobs) - ) - - def stop_viewer(self): - """Stops the job viewer.""" - if self.job_viewer: - self.job_viewer.close() - self.job_viewer = None - - def start_viewer(self): - """Starts the job viewer""" - self.job_viewer = build_job_viewer() - self.refresh_viewer() - - def update_single_job(self, update_info): - """Update a single job instance - - Args: - update_info (tuple): Updated job info. - """ - job_id = update_info[0] - found_job = False - ind = None - for idx, job in enumerate(self.jobs): - if job.job_id == job_id: - found_job = True - ind = idx - break - if found_job: - job_wid = self.jobs[ind] - # update status - if update_info[1] == "DONE": - stat = f"{update_info[1]}" - elif update_info[1] == "ERROR": - stat = f"{update_info[1]}" - elif update_info[1] == "CANCELLED": - stat = f"{update_info[1]}" - else: - stat = update_info[1] - job_wid.children[3].value = stat - # update queue - if update_info[2] == 0: - queue = "-" - else: - queue = str(update_info[2]) - job_wid.children[4].value = queue - # update msg - job_wid.children[5].value = update_info[3] - - def cancel_job(self, job_id): - """Cancels a job in the watcher - - Args: - job_id (str): Job id to remove. - - Raises: - Exception: Job id not found. - """ - from qiskit.providers.ibmq.job.exceptions import ( # pylint: disable=no-name-in-module - IBMQJobApiError, - ) - - do_pop = False - ind = None - for idx, job in enumerate(self.jobs): - if job.job_id == job_id: - do_pop = True - ind = idx - break - if not do_pop: - raise Exception("job_id not found") - if "CANCELLED" not in self.jobs[ind].children[3].value: - try: - self.jobs[ind].job.cancel() - status = self.jobs[ind].job.status() - except IBMQJobApiError: - pass - else: - self.update_single_job((self.jobs[ind].job_id, status.name, 0, status.value)) - - def clear_done(self): - """Clears the done jobs from the list.""" - _temp_jobs = [] - do_refresh = False - for job in self.jobs: - job_str = job.children[3].value - if not (("DONE" in job_str) or ("CANCELLED" in job_str) or ("ERROR" in job_str)): - _temp_jobs.append(job) - else: - job.close() - do_refresh = True - if do_refresh: - self.jobs = _temp_jobs - self.refresh_viewer() - - def _init_subscriber(self): - def _add_job(job): - status = job.status() - job_widget = create_job_widget( - self, job, job.backend(), status.name, job.queue_position(), status.value - ) - self.jobs.append(job_widget) - self.refresh_viewer() - _job_monitor(job, status, self) - - self.subscribe("ibmq.job.start", _add_job) - - -@magics_class -class JobWatcherMagic(Magics): - """A class for enabling/disabling the job watcher.""" - - @line_magic - def qiskit_job_watcher(self, line="", cell=None): - """A Jupyter magic function to enable job watcher.""" - _JOB_WATCHER.stop_viewer() - _JOB_WATCHER.start_viewer() - - @line_magic - def qiskit_disable_job_watcher(self, line="", cell=None): - """A Jupyter magic function to disable job watcher.""" - _JOB_WATCHER.stop_viewer() - - -if _optionals.HAS_IBMQ: - # The Jupyter job watcher instance - _JOB_WATCHER = JobWatcher() diff --git a/qiskit/tools/jupyter/job_widgets.py b/qiskit/tools/jupyter/job_widgets.py deleted file mode 100644 index f854d5ee1031..000000000000 --- a/qiskit/tools/jupyter/job_widgets.py +++ /dev/null @@ -1,160 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A module of widgets for job tracking""" - -import ipywidgets as widgets -from IPython.display import display, Javascript - - -def make_clear_button(watcher): - """Makes the clear button - - Args: - watcher (widget): The watcher widget instance. - - Returns: - widget: The clear button widget. - """ - clear = widgets.Button( - description="Clear", - button_style="primary", - layout=widgets.Layout(width="70px", grid_area="right", padding="0px 0px 0px 0px"), - ) - - def on_clear_button_clicked(_): - watcher.clear_done() - - clear.on_click(on_clear_button_clicked) - - clear_button = widgets.GridBox( - children=[clear], - layout=widgets.Layout( - width="100%", - grid_template_columns="20% 20% 20% 20% 20%", - grid_template_areas=""" - ". . . . right " - """, - ), - ) - return clear_button - - -def make_labels(): - """Makes the labels widget. - - Returns: - widget: The labels widget. - """ - labels0 = widgets.HTML(value="
Job ID
", layout=widgets.Layout(width="190px")) - labels1 = widgets.HTML(value="
Backend
", layout=widgets.Layout(width="145px")) - labels2 = widgets.HTML(value="
Status
", layout=widgets.Layout(width="95px")) - labels3 = widgets.HTML(value="
Queue
", layout=widgets.Layout(width="70px")) - labels4 = widgets.HTML(value="
Message
") - - labels = widgets.HBox( - children=[labels0, labels1, labels2, labels3, labels4], - layout=widgets.Layout(width="600px", margin="0px 0px 0px 37px"), - ) - return labels - - -def create_job_widget(watcher, job, backend, status="", queue_pos=None, msg=""): - """Creates a widget corresponding to a particular job instance. - - Args: - watcher (widget): The job watcher instance. - job (IBMQJob): The job. - backend (str): The backend the job is running on. - status (str): The job status. - queue_pos (int): Queue position, if any. - msg (str): Job message, if any. - - Returns: - widget: The job widget - """ - job_id = job.job_id() - - id_label = widgets.HTML(value=f"{job_id}", layout=widgets.Layout(width="190px")) - backend_label = widgets.HTML(value=f"{backend}", layout=widgets.Layout(width="145px")) - status_label = widgets.HTML(value=f"{status}", layout=widgets.Layout(width="95px")) - if queue_pos is None: - queue_pos = "-" - else: - queue_pos = str(queue_pos) - queue_label = widgets.HTML(value=f"{queue_pos}", layout=widgets.Layout(width="70px")) - - msg_label = widgets.HTML( - value=f"

{msg}

", - layout=widgets.Layout(overflow_x="scroll"), - ) - - close_button = widgets.Button( - button_style="", icon="close", layout=widgets.Layout(width="32px", margin="0px 5px 0px 0px") - ) - close_button.style.button_color = "white" - - def cancel_on_click(_): - watcher.cancel_job(job_id) - - close_button.on_click(cancel_on_click) - - job_grid = widgets.HBox( - children=[close_button, id_label, backend_label, status_label, queue_label, msg_label], - layout=widgets.Layout(min_width="700px", max_width="700px"), - ) - job_grid.job_id = job_id - job_grid.job = job - return job_grid - - -def build_job_viewer(): - """Builds the job viewer widget - - Returns: - widget: Job viewer. - """ - acc = widgets.Accordion( - children=[widgets.VBox(layout=widgets.Layout(max_width="710px", min_width="710px"))], - layout=widgets.Layout( - width="auto", - max_width="750px", - max_height="500px", - overflow_y="scroll", - overflow_x="hidden", - ), - ) - acc.set_title(0, "IBMQ Jobs") - acc.selected_index = None - acc.layout.visibility = "hidden" - display(acc) - acc._dom_classes = ["job_widget"] - display( - Javascript( - """ - const isLab = window['Jupyter'] === undefined; - const notebook = document.querySelector( isLab ? 'div.jp-Notebook' : '#site'); - const jobWidget = document.querySelector('div.job_widget'); - notebook.prepend(jobWidget); - jobWidget.style.zIndex = '999'; - jobWidget.style.position = isLab ? 'sticky' : 'fixed'; - jobWidget.style.boxShadow = '5px 5px 5px -3px black'; - jobWidget.style.opacity = '0.95'; - if (isLab) { - jobWidget.style.top = '0'; - jobWidget.style.left = '0'; - } - """ - ) - ) - acc.layout.visibility = "visible" - return acc diff --git a/qiskit/tools/jupyter/jupyter_magics.py b/qiskit/tools/jupyter/jupyter_magics.py deleted file mode 100644 index 5fc9d92b7bb6..000000000000 --- a/qiskit/tools/jupyter/jupyter_magics.py +++ /dev/null @@ -1,190 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A module of magic functions""" - -import time -import threading -from IPython import get_ipython -from IPython.display import display -from IPython.core import magic_arguments -from IPython.core.magic import cell_magic, line_magic, Magics, magics_class, register_line_magic - -from qiskit.utils import optionals as _optionals -from qiskit.utils.deprecation import deprecate_func -import qiskit -from qiskit.tools.events.progressbar import TextProgressBar -from .progressbar import HTMLProgressBar -from .library import circuit_library_widget - - -def _html_checker(job_var, interval, status, header, _interval_set=False): - """Internal function that updates the status - of a HTML job monitor. - - Args: - job_var (BaseJob): The job to keep track of. - interval (int): The status check interval - status (widget): HTML ipywidget for output to screen - header (str): String representing HTML code for status. - _interval_set (bool): Was interval set by user? - """ - job_status = job_var.status() - job_status_name = job_status.name - job_status_msg = job_status.value - status.value = header % (job_status_msg) - while job_status_name not in ["DONE", "CANCELLED"]: - time.sleep(interval) - job_status = job_var.status() - job_status_name = job_status.name - job_status_msg = job_status.value - if job_status_name == "ERROR": - break - if job_status_name == "QUEUED": - job_status_msg += " (%s)" % job_var.queue_position() - if job_var.queue_position() is None: - interval = 2 - elif not _interval_set: - interval = max(job_var.queue_position(), 2) - else: - if not _interval_set: - interval = 2 - status.value = header % (job_status_msg) - - status.value = header % (job_status_msg) - - -@magics_class -class StatusMagic(Magics): - """A class of status magic functions.""" - - @cell_magic - @magic_arguments.magic_arguments() - @magic_arguments.argument( - "-i", "--interval", type=float, default=None, help="Interval for status check." - ) - @_optionals.HAS_IPYWIDGETS.require_in_call - def qiskit_job_status(self, line="", cell=None): - """A Jupyter magic function to check the status of a Qiskit job instance.""" - import ipywidgets as widgets - - args = magic_arguments.parse_argstring(self.qiskit_job_status, line) - - if args.interval is None: - args.interval = 2 - _interval_set = False - else: - _interval_set = True - - # Split cell lines to get LHS variables - cell_lines = cell.split("\n") - line_vars = [] - for cline in cell_lines: - if "=" in cline and "==" not in cline: - line_vars.append(cline.replace(" ", "").split("=")[0]) - elif ".append(" in cline: - line_vars.append(cline.replace(" ", "").split("(")[0]) - - # Execute the cell - self.shell.ex(cell) - - # Look for all vars that are BaseJob instances - jobs = [] - for var in line_vars: - iter_var = False - if "#" not in var: - # The line var is a list or array, but we cannot parse the index - # so just iterate over the whole array for jobs. - if "[" in var: - var = var.split("[")[0] - iter_var = True - elif ".append" in var: - var = var.split(".append")[0] - iter_var = True - - if iter_var: - for item in self.shell.user_ns[var]: - if isinstance(item, qiskit.providers.job.Job): - jobs.append(item) - else: - if isinstance(self.shell.user_ns[var], qiskit.providers.job.Job): - jobs.append(self.shell.user_ns[var]) - - # Must have one job class - if not any(jobs): - raise Exception("Cell must contain at least one variable of BaseJob type.") - - # List index of job if checking status of multiple jobs. - multi_job = False - if len(jobs) > 1: - multi_job = True - - job_checkers = [] - # Loop over every BaseJob that was found. - for idx, job_var in enumerate(jobs): - style = "font-size:16px;" - if multi_job: - idx_str = "[%s]" % idx - else: - idx_str = "" - header = f"

Job Status {idx_str}: %s

" - status = widgets.HTML(value=header % job_var.status().value) - - thread = threading.Thread( - target=_html_checker, args=(job_var, args.interval, status, header, _interval_set) - ) - thread.start() - job_checkers.append(status) - - # Group all HTML widgets into single vertical layout - box = widgets.VBox(job_checkers) - display(box) - - -@magics_class -class ProgressBarMagic(Magics): - """A class of progress bar magic functions.""" - - @line_magic - @magic_arguments.magic_arguments() - @magic_arguments.argument( - "-t", "--type", type=str, default="html", help="Type of progress bar, 'html' or 'text'." - ) - def qiskit_progress_bar(self, line="", cell=None): # pylint: disable=unused-argument - """A Jupyter magic function to generate progressbar.""" - args = magic_arguments.parse_argstring(self.qiskit_progress_bar, line) - if args.type == "html": - pbar = HTMLProgressBar() - elif args.type == "text": - pbar = TextProgressBar() - else: - raise qiskit.QiskitError("Invalid progress bar type.") - - return pbar - - -if _optionals.HAS_MATPLOTLIB and get_ipython(): - - @register_line_magic - @deprecate_func( - since="0.25.0", - additional_msg="This was originally only for internal documentation and is no longer used.", - ) - def circuit_library_info(circuit: qiskit.QuantumCircuit) -> None: - """Displays library information for a quantum circuit. - - Args: - circuit: Input quantum circuit. - """ - shell = get_ipython() - circ = shell.ev(circuit) - circuit_library_widget(circ) diff --git a/qiskit/tools/jupyter/library.py b/qiskit/tools/jupyter/library.py deleted file mode 100644 index 57a27ece8cdc..000000000000 --- a/qiskit/tools/jupyter/library.py +++ /dev/null @@ -1,189 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# pylint: disable=invalid-name,no-name-in-module,ungrouped-imports - -"""A circuit library widget module""" - -import ipywidgets as wid -from IPython.display import display -from qiskit import QuantumCircuit -from qiskit.utils import optionals as _optionals -from qiskit.utils.deprecation import deprecate_func - - -@_optionals.HAS_MATPLOTLIB.require_in_call -def _generate_circuit_library_visualization(circuit: QuantumCircuit): - import matplotlib.pyplot as plt - - circuit = circuit.decompose() - ops = circuit.count_ops() - num_nl = circuit.num_nonlocal_gates() - _fig, (ax0, ax1) = plt.subplots(2, 1) - circuit.draw("mpl", ax=ax0) - ax1.axis("off") - ax1.grid(visible=None) - ax1.table( - [[circuit.name], [circuit.width()], [circuit.depth()], [sum(ops.values())], [num_nl]], - rowLabels=["Circuit Name", "Width", "Depth", "Total Gates", "Non-local Gates"], - ) - plt.tight_layout() - plt.show() - - -@deprecate_func( - since="0.25.0", - additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", - package_name="qiskit-terra", -) -def circuit_data_table(circuit: QuantumCircuit) -> wid.HTML: - """Create a HTML table widget for a given quantum circuit. - - Args: - circuit: Input quantum circuit. - - Returns: - Output widget. - """ - - circuit = circuit.decompose() - ops = circuit.count_ops() - num_nl = circuit.num_nonlocal_gates() - - html = "" - html += """""" - html += f"" - html += f"" - html += f"" - html += f"" - html += f"" - html += "
{circuit.name}
Width{circuit.width()}
Depth{circuit.depth()}
Total Gates{sum(ops.values())}
Non-local Gates{num_nl}
" - - out_wid = wid.HTML(html) - return out_wid - - -head_style = ( - "font-family: IBM Plex Sans, Arial, Helvetica, sans-serif;" - " font-size: 20px; font-weight: medium;" -) - -property_label = wid.HTML( - f"

Circuit Properties

", - layout=wid.Layout(margin="0px 0px 10px 0px"), -) - - -@deprecate_func( - since="0.25.0", - additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", - package_name="qiskit-terra", -) -def properties_widget(circuit: QuantumCircuit) -> wid.VBox: - """Create a HTML table widget with header for a given quantum circuit. - - Args: - circuit: Input quantum circuit. - - Returns: - Output widget. - """ - properties = wid.VBox( - children=[property_label, circuit_data_table(circuit)], - layout=wid.Layout(width="40%", height="auto"), - ) - return properties - - -@deprecate_func( - since="0.25.0", - additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", - package_name="qiskit-terra", -) -def circuit_diagram_widget() -> wid.Box: - """Create a circuit diagram widget. - - Returns: - Output widget. - """ - # The max circuit height corresponds to a 20Q circuit with flat - # classical register. - top_out = wid.Output( - layout=wid.Layout( - width="100%", - height="auto", - max_height="1000px", - overflow="hidden scroll", - ) - ) - - top = wid.Box(children=[top_out], layout=wid.Layout(width="100%", height="auto")) - - return top - - -@deprecate_func( - since="0.25.0", - additional_msg="This is unused by Qiskit, and no replacement will be publicly provided.", - package_name="qiskit-terra", -) -def circuit_library_widget(circuit: QuantumCircuit) -> None: - """Create a circuit library widget. - - Args: - circuit: Input quantum circuit. - """ - sep_length = str(min(20, 495)) - - # The separator widget - sep = wid.HTML( - f"
", - layout=wid.Layout(height="auto", max_height="495px", margin="40px 0px 0px 20px"), - ) - bottom = wid.HBox( - children=[properties_widget(circuit), sep], - layout=wid.Layout(max_height="550px", height="auto"), - ) - - top = circuit_diagram_widget() - - with top.children[0]: - display(circuit.decompose().draw(output="mpl")) - - display(wid.VBox(children=[top, bottom], layout=wid.Layout(width="100%", height="auto"))) diff --git a/qiskit/tools/jupyter/monospace.py b/qiskit/tools/jupyter/monospace.py deleted file mode 100644 index 0ff0b99c8bfb..000000000000 --- a/qiskit/tools/jupyter/monospace.py +++ /dev/null @@ -1,29 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# pylint: disable=unused-argument - -"""A Jupyter magic to choose a real monospaced fonts, if available.""" - -from IPython.display import HTML, display -from IPython.core.magic import line_magic, Magics, magics_class - - -@magics_class -class MonospacedOutput(Magics): - """A class for setting "Courier New" for output code.""" - - @line_magic - def monospaced_output(self, line="", cell=None): - """A Jupyter magic function to set "Courier New" for output code.""" - html = """""" - display(HTML(html)) diff --git a/qiskit/tools/jupyter/progressbar.py b/qiskit/tools/jupyter/progressbar.py deleted file mode 100644 index 06531935d2de..000000000000 --- a/qiskit/tools/jupyter/progressbar.py +++ /dev/null @@ -1,122 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -"""Progress bars module""" - -import time - -from qiskit.tools.events.progressbar import BaseProgressBar -from qiskit.utils import optionals as _optionals - - -@_optionals.HAS_IPYWIDGETS.require_in_instance -class HTMLProgressBar(BaseProgressBar): - """ - A simple HTML progress bar for using in IPython notebooks. - """ - - def __init__(self): - super().__init__() - self.progress_bar = None - self.label = None - self.box = None - self._init_subscriber() - - def _init_subscriber(self): - def _initialize_progress_bar(num_tasks): - """When an event of compilation starts, this function will be called, and - will initialize the progress bar. - - Args: - num_tasks: Number of compilation tasks the progress bar will track - """ - self.start(num_tasks) - - self.subscribe("terra.parallel.start", _initialize_progress_bar) - - def _update_progress_bar(progress): - """When an event of compilation completes, this function will be called, and - will update the progress bar indication. - - Args: - progress: Number of tasks completed - """ - self.update(progress) - - self.subscribe("terra.parallel.done", _update_progress_bar) - - def _finish_progress_bar(): - """When an event of compilation finishes (meaning that there's no more circuits to - compile), this function will be called, unsubscribing from all events and - finishing the progress bar.""" - self.unsubscribe("terra.parallel.start", _initialize_progress_bar) - self.unsubscribe("terra.parallel.done", _update_progress_bar) - self.unsubscribe("terra.parallel.finish", _finish_progress_bar) - self.finished() - - self.subscribe("terra.parallel.finish", _finish_progress_bar) - - def start(self, iterations): - import ipywidgets as widgets - from IPython.display import display - - self.touched = True - self.iter = int(iterations) - self.t_start = time.time() - self.progress_bar = widgets.IntProgress(min=0, max=self.iter, value=0) - self.progress_bar.bar_style = "info" - self.label = widgets.HTML() - self.box = widgets.VBox(children=[self.label, self.progress_bar]) - display(self.box) - - def update(self, n): - self.progress_bar.value += 1 - lbl = "Completed %s/%s: Est. remaining time: %s." - self.label.value = lbl % (n, self.iter, self.time_remaining_est(n)) - - def finished(self): - self.t_done = time.time() - self.progress_bar.bar_style = "success" - self.label.value = "Elapsed time: %s" % self.time_elapsed() diff --git a/qiskit/tools/jupyter/version_table.py b/qiskit/tools/jupyter/version_table.py deleted file mode 100644 index 9d54ed080946..000000000000 --- a/qiskit/tools/jupyter/version_table.py +++ /dev/null @@ -1,67 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2021. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. -# pylint: disable=unused-argument - -"""A module for monitoring backends.""" - -import time -from sys import modules -from IPython.display import HTML, display -from IPython.core.magic import line_magic, Magics, magics_class -import qiskit -from qiskit.utils import local_hardware_info - - -@magics_class -class VersionTable(Magics): - """A class of status magic functions.""" - - @line_magic - def qiskit_version_table(self, line="", cell=None): - """ - Print an HTML-formatted table with version numbers for Qiskit and its - dependencies. This should make it possible to reproduce the environment - and the calculation later on. - """ - html = "

Version Information

" - html += "" - html += "" - - packages = {"qiskit": qiskit.__version__} - qiskit_modules = {module.split(".")[0] for module in modules.keys() if "qiskit" in module} - - for qiskit_module in qiskit_modules: - packages[qiskit_module] = getattr(modules[qiskit_module], "__version__", None) - - for name, version in packages.items(): - if version: - html += f"" - - html += "" - - local_hw_info = local_hardware_info() - sys_info = [ - ("Python version", local_hw_info["python_version"]), - ("Python compiler", local_hw_info["python_compiler"]), - ("Python build", local_hw_info["python_build"]), - ("OS", "%s" % local_hw_info["os"]), - ("CPUs", "%s" % local_hw_info["cpus"]), - ("Memory (Gb)", "%s" % local_hw_info["memory"]), - ] - - for name, version in sys_info: - html += f"" - - html += "" % time.strftime("%a %b %d %H:%M:%S %Y %Z") - html += "
SoftwareVersion
{name}{version}
System information
{name}{version}
%s
" - - return display(HTML(html)) diff --git a/qiskit/tools/jupyter/watcher_monitor.py b/qiskit/tools/jupyter/watcher_monitor.py deleted file mode 100644 index 7d12dfc9bd03..000000000000 --- a/qiskit/tools/jupyter/watcher_monitor.py +++ /dev/null @@ -1,74 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A module of widgets for job monitoring""" -import sys -import time -import threading - - -def _job_monitor(job, status, watcher): - """Monitor the status of a IBMQJob instance. - - Args: - job (BaseJob): Job to monitor. - status (Enum): Job status. - watcher (JobWatcher): Job watcher instance - """ - thread = threading.Thread(target=_job_checker, args=(job, status, watcher)) - thread.start() - - -def _job_checker(job, status, watcher): - """A simple job status checker - - Args: - job (BaseJob): The job to check. - status (Enum): Job status. - watcher (JobWatcher): Job watcher instance - - """ - prev_status_name = None - prev_queue_pos = None - interval = 2 - exception_count = 0 - - while status.name not in ["DONE", "CANCELLED", "ERROR"]: - time.sleep(interval) - try: - status = job.status() - exception_count = 0 - - if status.name == "QUEUED": - queue_pos = job.queue_position() - if queue_pos != prev_queue_pos: - - update_info = (job.job_id(), status.name, queue_pos, status.value) - - watcher.update_single_job(update_info) - interval = max(queue_pos, 2) - prev_queue_pos = queue_pos - - elif status.name != prev_status_name: - update_info = (job.job_id(), status.name, 0, status.value) - - watcher.update_single_job(update_info) - interval = 2 - prev_status_name = status.name - - # pylint: disable=broad-except - except Exception: - exception_count += 1 - if exception_count == 5: - update_info = (job.job_id(), "NA", 0, "Could not query job.") - watcher.update_single_job(update_info) - sys.exit() diff --git a/qiskit/tools/monitor/overview.py b/qiskit/tools/monitor/overview.py deleted file mode 100644 index 41853fb3ad26..000000000000 --- a/qiskit/tools/monitor/overview.py +++ /dev/null @@ -1,247 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A module for viewing the details of all available devices. -""" - -import math -from qiskit.exceptions import QiskitError, MissingOptionalLibraryError - - -def get_unique_backends(): - """Gets the unique backends that are available. - - Returns: - list: Unique available backends. - - Raises: - QiskitError: No backends available. - MissingOptionalLibraryError: If qiskit-ibmq-provider is not installed - """ - try: - from qiskit.providers.ibmq import IBMQ - except ImportError as ex: - raise MissingOptionalLibraryError( - libname="qiskit-ibmq-provider", - name="get_unique_backends", - pip_install="pip install qiskit-ibmq-provider", - ) from ex - backends = [] - for provider in IBMQ.providers(): - for backend in provider.backends(): - backends.append(backend) - unique_hardware_backends = [] - unique_names = [] - for back in backends: - if back.name() not in unique_names and not back.configuration().simulator: - unique_hardware_backends.append(back) - unique_names.append(back.name()) - if not unique_hardware_backends: - raise QiskitError("No backends available.") - return unique_hardware_backends - - -def backend_monitor(backend): - """Monitor a single IBMQ backend. - - Args: - backend (IBMQBackend): Backend to monitor. - Raises: - QiskitError: Input is not a IBMQ backend. - MissingOptionalLibraryError: If qiskit-ibmq-provider is not installed - - Examples: - .. code-block:: python - - from qiskit.providers.ibmq import IBMQ - from qiskit.tools.monitor import backend_monitor - provider = IBMQ.get_provider(hub='ibm-q') - backend_monitor(provider.backends.ibmq_lima) - """ - try: - from qiskit.providers.ibmq import IBMQBackend - except ImportError as ex: - raise MissingOptionalLibraryError( - libname="qiskit-ibmq-provider", - name="backend_monitor", - pip_install="pip install qiskit-ibmq-provider", - ) from ex - - if not isinstance(backend, IBMQBackend): - raise QiskitError("Input variable is not of type IBMQBackend.") - config = backend.configuration().to_dict() - status = backend.status().to_dict() - config_dict = {**status, **config} - - print(backend.name()) - print("=" * len(backend.name())) - print("Configuration") - print("-" * 13) - offset = " " - - upper_list = [ - "n_qubits", - "operational", - "status_msg", - "pending_jobs", - "backend_version", - "basis_gates", - "local", - "simulator", - ] - - lower_list = list(set(config_dict.keys()).difference(upper_list)) - # Remove gates because they are in a different tab - lower_list.remove("gates") - for item in upper_list + lower_list: - print(offset + item + ":", config_dict[item]) - - # Stop here if simulator - if config["simulator"]: - return - - print() - props = backend.properties() - qubit_header = None - sep = " / " - - for index, qubit_data in enumerate(props.qubits): - name = "Q%s" % index - gate_data = [gate for gate in props.gates if gate.qubits == [index]] - - cal_data = dict.fromkeys(["T1", "T2", "frequency", "readout_error"], "Unknown") - for nduv in qubit_data: - if nduv.name in cal_data: - cal_data[nduv.name] = format(nduv.value, ".5f") + " " + nduv.unit - - gate_names = [] - gate_error = [] - for gd in gate_data: - if gd.gate in ["id"]: - continue - try: - gate_error.append(format(props.gate_error(gd.gate, index), ".5f")) - gate_names.append(gd.gate.upper() + " err") - except QiskitError: - pass - - if not qubit_header: - qubit_header = ( - "Qubits [Name / Freq / T1 / T2" + sep.join([""] + gate_names) + " / Readout err]" - ) - print(qubit_header) - print("-" * len(qubit_header)) - - qstr = sep.join( - [name, cal_data["frequency"], cal_data["T1"], cal_data["T2"]] - + gate_error - + [cal_data["readout_error"]] - ) - - print(offset + qstr) - - print() - multi_qubit_gates = [g for g in props.gates if len(g.qubits) > 1] - multi_header = "Multi-Qubit Gates [Name / Type / Gate Error]" - print(multi_header) - print("-" * len(multi_header)) - - for gate in multi_qubit_gates: - qubits = gate.qubits - ttype = gate.gate - error = "Unknown" - try: - error = format(props.gate_error(gate.gate, qubits), ".5f") - except QiskitError: - pass - mstr = sep.join([f"{ttype}{qubits[0]}_{qubits[1]}", ttype, str(error)]) - print(offset + mstr) - - -def backend_overview(): - """Gives overview information on all the IBMQ - backends that are available. - - Examples: - - .. code-block:: python - - from qiskit.providers.ibmq import IBMQ - from qiskit.tools.monitor import backend_overview - provider = IBMQ.get_provider(hub='ibm-q') - backend_overview() - """ - unique_hardware_backends = get_unique_backends() - _backends = [] - # Sort backends by operational or not - for idx, back in enumerate(unique_hardware_backends): - if back.status().operational: - _backends = [back] + _backends - else: - _backends = _backends + [back] - - stati = [back.status() for back in _backends] - idx = list(range(len(_backends))) - pending = [s.pending_jobs for s in stati] - _, least_idx = zip(*sorted(zip(pending, idx))) - - # Make sure least pending is operational - for ind in least_idx: - if stati[ind].operational: - least_pending_idx = ind - break - - num_rows = math.ceil(len(_backends) / 3) - - count = 0 - num_backends = len(_backends) - for _ in range(num_rows): - max_len = 0 - str_list = [""] * 8 - for idx in range(3): - offset = " " * 10 if idx else "" - config = _backends[count].configuration().to_dict() - props = _backends[count].properties().to_dict() - num_qubits = config["n_qubits"] - str_list[0] += " " * (max_len - len(str_list[0])) + offset - str_list[0] += _backends[count].name() - - str_list[1] += " " * (max_len - len(str_list[1])) + offset - str_list[1] += "-" * len(_backends[count].name()) - - str_list[2] += " " * (max_len - len(str_list[2])) + offset - str_list[2] += "Num. Qubits: %s" % config["n_qubits"] - - str_list[3] += " " * (max_len - len(str_list[3])) + offset - str_list[3] += "Pending Jobs: %s" % stati[count].pending_jobs - - str_list[4] += " " * (max_len - len(str_list[4])) + offset - str_list[4] += "Least busy: %s" % (count == least_pending_idx) - - str_list[5] += " " * (max_len - len(str_list[5])) + offset - str_list[5] += "Operational: %s" % stati[count].operational - - str_list[6] += " " * (max_len - len(str_list[6])) + offset - str_list[6] += "Avg. T1: %s" % round( - sum(q[0]["value"] for q in props["qubits"]) / num_qubits, 1 - ) - str_list[7] += " " * (max_len - len(str_list[7])) + offset - str_list[7] += "Avg. T2: %s" % round( - sum(q[1]["value"] for q in props["qubits"]) / num_qubits, 1 - ) - count += 1 - if count == num_backends: - break - max_len = max(len(s) for s in str_list) - - print("\n".join(str_list)) - print("\n" * 2) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 71f596c95dad..bfc0e622d5d2 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -1231,15 +1231,6 @@ InstructionDurations -Fenced Objects --------------- - -.. autosummary:: - :toctree: ../stubs/ - - FencedPropertySet - FencedDAGCircuit - Abstract Passes --------------- @@ -1262,14 +1253,13 @@ # For backward compatibility from qiskit.passmanager import ( - FlowController, ConditionalController, DoWhileController, ) +from qiskit.passmanager.compilation_status import PropertySet from .passmanager import PassManager, StagedPassManager from .passmanager_config import PassManagerConfig -from .propertyset import PropertySet # pylint: disable=no-name-in-module from .exceptions import ( TranspilerError, TranspilerAccessError, @@ -1277,7 +1267,6 @@ LayoutError, CircuitTooWideForTarget, ) -from .fencedobjs import FencedDAGCircuit, FencedPropertySet from .basepasses import AnalysisPass, TransformationPass from .coupling import CouplingMap from .layout import Layout, TranspileLayout diff --git a/qiskit/transpiler/fencedobjs.py b/qiskit/transpiler/fencedobjs.py deleted file mode 100644 index 8113e58d76d9..000000000000 --- a/qiskit/transpiler/fencedobjs.py +++ /dev/null @@ -1,78 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Fenced objects are wraps for raising TranspilerError when they are modified.""" - -from qiskit.utils.deprecation import deprecate_func -from .exceptions import TranspilerError - - -class FencedObject: - """Given an instance and a list of attributes to fence, raises a TranspilerError when one - of these attributes is accessed.""" - - @deprecate_func( - since="0.45.0", - additional_msg=( - "Internal use of FencedObject is already removed from pass manager. " - "Implementation of a task subclass with protection for input object modification " - "is now responsibility of the developer." - ), - pending=True, - ) - def __init__(self, instance, attributes_to_fence): - self._wrapped = instance - self._attributes_to_fence = attributes_to_fence - - def __getattribute__(self, name): - object.__getattribute__(self, "_check_if_fenced")(name) - return getattr(object.__getattribute__(self, "_wrapped"), name) - - def __getitem__(self, key): - object.__getattribute__(self, "_check_if_fenced")("__getitem__") - return object.__getattribute__(self, "_wrapped")[key] - - def __setitem__(self, key, value): - object.__getattribute__(self, "_check_if_fenced")("__setitem__") - object.__getattribute__(self, "_wrapped")[key] = value - - def _check_if_fenced(self, name): - """ - Checks if the attribute name is in the list of attributes to protect. If so, raises - TranspilerError. - - Args: - name (string): the attribute name to check - - Raises: - TranspilerError: when name is the list of attributes to protect. - """ - if name in object.__getattribute__(self, "_attributes_to_fence"): - raise TranspilerError( - "The fenced %s has the property %s protected" - % (type(object.__getattribute__(self, "_wrapped")), name) - ) - - -class FencedPropertySet(FencedObject): - """A property set that cannot be written (via __setitem__)""" - - def __init__(self, property_set_instance): - super().__init__(property_set_instance, ["__setitem__"]) - - -class FencedDAGCircuit(FencedObject): - """A dag circuit that cannot be modified (via remove_op_node)""" - - # FIXME: add more fenced methods of the dag after dagcircuit rewrite - def __init__(self, dag_circuit_instance): - super().__init__(dag_circuit_instance, ["remove_op_node"]) diff --git a/qiskit/transpiler/passmanager.py b/qiskit/transpiler/passmanager.py index cf7c44d69a7d..7757282bc10d 100644 --- a/qiskit/transpiler/passmanager.py +++ b/qiskit/transpiler/passmanager.py @@ -16,7 +16,6 @@ import inspect import io import re -import warnings from collections.abc import Iterator, Iterable, Callable from functools import wraps from typing import Union, List, Any @@ -25,14 +24,12 @@ from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit.dagcircuit import DAGCircuit from qiskit.passmanager.passmanager import BasePassManager -from qiskit.passmanager.base_tasks import Task, BaseController -from qiskit.passmanager.flow_controllers import FlowController +from qiskit.passmanager.base_tasks import Task +from qiskit.passmanager.flow_controllers import FlowControllerLinear from qiskit.passmanager.exceptions import PassManagerError -from qiskit.utils.deprecation import deprecate_arg from .basepasses import BasePass from .exceptions import TranspilerError from .layout import TranspileLayout -from .runningpassmanager import RunningPassManager _CircuitsT = Union[List[QuantumCircuit], QuantumCircuit] @@ -52,9 +49,6 @@ def __init__( max_iteration: The maximum number of iterations the schedule will be looped if the condition is not met. """ - # For backward compatibility. - self._pass_sets = [] - super().__init__( tasks=passes, max_iteration=max_iteration, @@ -102,140 +96,32 @@ def _passmanager_backend( return out_program - @deprecate_arg( - name="max_iteration", - since="0.25", - additional_msg="'max_iteration' can be set in the constructor.", - pending=True, - package_name="qiskit-terra", - ) def append( self, passes: Task | list[Task], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: """Append a Pass Set to the schedule of passes. Args: - passes: A set of passes (a pass set) to be added to schedule. A pass set is a list of - passes that are controlled by the same flow controller. If a single pass is - provided, the pass set will only have that pass a single element. - It is also possible to append a :class:`.BaseFlowController` instance and - the rest of the parameter will be ignored. - max_iteration: max number of iterations of passes. - flow_controller_conditions: Dictionary of control flow plugins. - Following built-in controllers are available by default: - - * do_while: The passes repeat until the callable returns False. Corresponds to - :class:`.DoWhileController`. - * condition: The passes run only if the callable returns True. Corresponds to - :class:`.ConditionalController`. - - In general, you have more control simply by creating the controller you want and - passing it to :meth:`append`. + passes: A set of transpiler passes to be added to schedule. Raises: TranspilerError: if a pass in passes is not a proper pass. """ - if max_iteration: - self.max_iteration = max_iteration - - # Backward compatibility as of Terra 0.25 - if isinstance(passes, Task): - passes = [passes] - self._pass_sets.append( - { - "passes": passes, - "flow_controllers": flow_controller_conditions, - } - ) - if flow_controller_conditions: - passes = _legacy_build_flow_controller( - passes, - options={"max_iteration": self.max_iteration}, - **flow_controller_conditions, - ) - - super().append(passes) + super().append(tasks=passes) - @deprecate_arg( - name="max_iteration", - since="0.25", - additional_msg="'max_iteration' can be set in the constructor.", - pending=True, - package_name="qiskit-terra", - ) def replace( self, index: int, passes: Task | list[Task], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: """Replace a particular pass in the scheduler. Args: index: Pass index to replace, based on the position in passes(). passes: A pass set to be added to the pass manager schedule. - max_iteration: max number of iterations of passes. - flow_controller_conditions: Dictionary of control flow plugins. - See :meth:`qiskit.transpiler.PassManager.append` for details. """ - if max_iteration: - self.max_iteration = max_iteration - - # Backward compatibility as of Terra 0.25 - if isinstance(passes, Task): - passes = [passes] - try: - self._pass_sets[index] = { - "passes": passes, - "flow_controllers": flow_controller_conditions, - } - except IndexError as ex: - raise PassManagerError(f"Index to replace {index} does not exists") from ex - if flow_controller_conditions: - passes = _legacy_build_flow_controller( - passes, - options={"max_iteration": self.max_iteration}, - **flow_controller_conditions, - ) - - super().replace(index, passes) - - def remove(self, index: int) -> None: - super().remove(index) - - # Backward compatibility as of Terra 0.25 - del self._pass_sets[index] - - def __getitem__(self, index): - new_passmanager = super().__getitem__(index) - - # Backward compatibility as of Terra 0.25 - _pass_sets = self._pass_sets[index] - if isinstance(_pass_sets, dict): - _pass_sets = [_pass_sets] - new_passmanager._pass_sets = _pass_sets - return new_passmanager - - def __add__(self, other): - new_passmanager = super().__add__(other) - - # Backward compatibility as of Terra 0.25 - if isinstance(other, self.__class__): - new_passmanager._pass_sets = self._pass_sets - new_passmanager._pass_sets += other._pass_sets - - # When other is not identical type, _pass_sets is also evaluated by self.append. - return new_passmanager - - def to_flow_controller(self) -> RunningPassManager: - # For backward compatibility. - # This method will be resolved to the base class and return FlowControllerLinear - flatten_tasks = list(self._flatten_tasks(self._tasks)) - return RunningPassManager(flatten_tasks) + super().replace(index, tasks=passes) # pylint: disable=arguments-differ def run( @@ -320,22 +206,6 @@ def draw(self, filename=None, style=None, raw=False): return pass_manager_drawer(self, filename=filename, style=style, raw=raw) - def passes(self) -> list[dict[str, BasePass]]: - """Return a list structure of the appended passes and its options. - - Returns: - A list of pass sets, as defined in ``append()``. - """ - ret = [] - for pass_set in self._pass_sets: - item = {"passes": pass_set["passes"]} - if pass_set["flow_controllers"]: - item["flow_controllers"] = set(pass_set["flow_controllers"].keys()) - else: - item["flow_controllers"] = {} - ret.append(item) - return ret - class StagedPassManager(PassManager): """A pass manager pipeline built from individual stages. @@ -462,12 +332,10 @@ def _generate_expanded_stages(self) -> Iterator[str]: def _update_passmanager(self) -> None: self._tasks = [] - self._pass_sets = [] for stage in self.expanded_stages: pm = getattr(self, stage, None) if pm is not None: self._tasks += pm._tasks - self._pass_sets.extend(pm._pass_sets) def __setattr__(self, attr, value): if value == self and attr in self.expanded_stages: @@ -479,8 +347,6 @@ def __setattr__(self, attr, value): def append( self, passes: Task | list[Task], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: raise NotImplementedError @@ -488,8 +354,6 @@ def replace( self, index: int, passes: BasePass | list[BasePass], - max_iteration: int = None, - **flow_controller_conditions: Any, ) -> None: raise NotImplementedError @@ -504,10 +368,6 @@ def __getitem__(self, index): # It returns instance of self.__class__ which is StagedPassManager. new_passmanager = PassManager(max_iteration=self.max_iteration) new_passmanager._tasks = self._tasks[index] - _pass_sets = self._pass_sets[index] - if isinstance(_pass_sets, dict): - _pass_sets = [_pass_sets] - new_passmanager._pass_sets = _pass_sets return new_passmanager def __len__(self): @@ -520,10 +380,6 @@ def __setitem__(self, index, item): def __add__(self, other): raise NotImplementedError - def passes(self) -> list[dict[str, BasePass]]: - self._update_passmanager() - return super().passes() - def run( self, circuits: _CircuitsT, @@ -533,6 +389,10 @@ def run( self._update_passmanager() return super().run(circuits, output_name, callback) + def to_flow_controller(self) -> FlowControllerLinear: + self._update_passmanager() + return super().to_flow_controller() + def draw(self, filename=None, style=None, raw=False): """Draw the staged pass manager.""" from qiskit.visualization import staged_pass_manager_drawer @@ -577,41 +437,3 @@ def _wrapped_callable(task, passmanager_ir, property_set, running_time, count): ) return _wrapped_callable - - -def _legacy_build_flow_controller( - tasks: list[Task], - options: dict[str, Any], - **flow_controller_conditions, -) -> BaseController: - """A legacy method to build flow controller with keyword arguments. - - Args: - tasks: A list of tasks fed into custom flow controllers. - options: Option for flow controllers. - flow_controller_conditions: Callables keyed on the alias of the flow controller. - - Returns: - A built controller. - """ - warnings.warn( - "Building a flow controller with keyword arguments is going to be deprecated. " - "Custom controllers must be explicitly instantiated and appended to the task list.", - PendingDeprecationWarning, - stacklevel=3, - ) - if isinstance(tasks, Task): - tasks = [tasks] - if any(not isinstance(t, Task) for t in tasks): - raise TypeError("Added tasks are not all valid pass manager task types.") - # Alias in higher hierarchy becomes outer controller. - for alias in FlowController.hierarchy[::-1]: - if alias not in flow_controller_conditions: - continue - class_type = FlowController.registered_controllers[alias] - init_kwargs = { - "options": options, - alias: flow_controller_conditions.pop(alias), - } - tasks = class_type(tasks, **init_kwargs) - return tasks diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index b21c7ac130f5..d24fdca64885 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -43,7 +43,7 @@ from qiskit.transpiler.passes import Depth, Size, FixedPoint, MinimumPoint from qiskit.transpiler.passes.utils.gates_basis import GatesInBasis from qiskit.transpiler.passes.synthesis.unitary_synthesis import UnitarySynthesis -from qiskit.passmanager.flow_controllers import ConditionalController +from qiskit.passmanager.flow_controllers import ConditionalController, DoWhileController from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason from qiskit.circuit.library.standard_gates import ( @@ -598,7 +598,7 @@ def _opt_control(property_set): else: raise TranspilerError(f"Invalid optimization_level: {optimization_level}") - unroll = [pass_ for x in translation.passes() for pass_ in x["passes"]] + unroll = translation.to_flow_controller() # Build nested Flow controllers def _unroll_condition(property_set): return not property_set["all_gates_in_basis"] @@ -618,7 +618,7 @@ def _unroll_condition(property_set): if optimization_level == 3 else _opt + _unroll_if_out_of_basis + _depth_check + _size_check ) - optimization.append(opt_loop, do_while=_opt_control) + optimization.append(DoWhileController(opt_loop, do_while=_opt_control)) return optimization else: return None @@ -712,13 +712,19 @@ def _swap_mapped(property_set): layout = PassManager() layout.append(_given_layout) if optimization_level == 0: - layout.append(TrivialLayout(coupling_map), condition=_choose_layout_condition) + layout.append( + ConditionalController( + TrivialLayout(coupling_map), condition=_choose_layout_condition + ) + ) layout += common.generate_embed_passmanager(coupling_map) return layout elif optimization_level == 1: layout.append( - [TrivialLayout(coupling_map), CheckMap(coupling_map)], - condition=_choose_layout_condition, + ConditionalController( + [TrivialLayout(coupling_map), CheckMap(coupling_map)], + condition=_choose_layout_condition, + ) ) choose_layout_1 = VF2Layout( coupling_map=pass_manager_config.coupling_map, @@ -728,7 +734,7 @@ def _swap_mapped(property_set): target=pass_manager_config.target, max_trials=2500, # Limits layout scoring to < 600ms on ~400 qubit devices ) - layout.append(choose_layout_1, condition=_layout_not_perfect) + layout.append(ConditionalController(choose_layout_1, condition=_layout_not_perfect)) choose_layout_2 = SabreLayout( coupling_map, max_iterations=2, @@ -739,13 +745,15 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - choose_layout_2, - ], - condition=_vf2_match_not_found, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_2, + ], + condition=_vf2_match_not_found, + ) ) elif optimization_level == 2: choose_layout_0 = VF2Layout( @@ -756,7 +764,9 @@ def _swap_mapped(property_set): target=pass_manager_config.target, max_trials=25000, # Limits layout scoring to < 10s on ~400 qubit devices ) - layout.append(choose_layout_0, condition=_choose_layout_condition) + layout.append( + ConditionalController(choose_layout_0, condition=_choose_layout_condition) + ) choose_layout_1 = SabreLayout( coupling_map, max_iterations=2, @@ -767,13 +777,15 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - choose_layout_1, - ], - condition=_vf2_match_not_found, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_1, + ], + condition=_vf2_match_not_found, + ) ) elif optimization_level == 3: choose_layout_0 = VF2Layout( @@ -784,7 +796,9 @@ def _swap_mapped(property_set): target=pass_manager_config.target, max_trials=250000, # Limits layout scoring to < 60s on ~400 qubit devices ) - layout.append(choose_layout_0, condition=_choose_layout_condition) + layout.append( + ConditionalController(choose_layout_0, condition=_choose_layout_condition) + ) choose_layout_1 = SabreLayout( coupling_map, max_iterations=4, @@ -795,21 +809,21 @@ def _swap_mapped(property_set): and pass_manager_config.routing_method != "sabre", ) layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - choose_layout_1, - ], - condition=_vf2_match_not_found, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + choose_layout_1, + ], + condition=_vf2_match_not_found, + ) ) else: raise TranspilerError(f"Invalid optimization level: {optimization_level}") embed = common.generate_embed_passmanager(coupling_map) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) + layout.append(ConditionalController(embed.to_flow_controller(), condition=_swap_mapped)) return layout @@ -829,7 +843,9 @@ def _choose_layout_condition(property_set): layout = PassManager() layout.append(_given_layout) - layout.append(TrivialLayout(coupling_map), condition=_choose_layout_condition) + layout.append( + ConditionalController(TrivialLayout(coupling_map), condition=_choose_layout_condition) + ) layout += common.generate_embed_passmanager(coupling_map) return layout @@ -851,12 +867,14 @@ def _choose_layout_condition(property_set): layout = PassManager() layout.append(_given_layout) layout.append( - DenseLayout( - coupling_map=pass_manager_config.coupling_map, - backend_prop=pass_manager_config.backend_properties, - target=pass_manager_config.target, - ), - condition=_choose_layout_condition, + ConditionalController( + DenseLayout( + coupling_map=pass_manager_config.coupling_map, + backend_prop=pass_manager_config.backend_properties, + target=pass_manager_config.target, + ), + condition=_choose_layout_condition, + ) ) layout += common.generate_embed_passmanager(coupling_map) return layout @@ -924,16 +942,16 @@ def _swap_mapped(property_set): else: raise TranspilerError(f"Invalid optimization level: {optimization_level}") layout.append( - [ - BarrierBeforeFinalMeasurements( - "qiskit.transpiler.internal.routing.protection.barrier" - ), - layout_pass, - ], - condition=_choose_layout_condition, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + "qiskit.transpiler.internal.routing.protection.barrier" + ), + layout_pass, + ], + condition=_choose_layout_condition, + ) ) embed = common.generate_embed_passmanager(coupling_map) - layout.append( - [pass_ for x in embed.passes() for pass_ in x["passes"]], condition=_swap_mapped - ) + layout.append(ConditionalController(embed.to_flow_controller(), condition=_swap_mapped)) return layout diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index 9219854a16a3..c00824a70374 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -20,6 +20,7 @@ from qiskit.circuit.controlflow import CONTROL_FLOW_OP_NAMES from qiskit.utils.deprecation import deprecate_func +from qiskit.passmanager.flow_controllers import ConditionalController from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.passes import Error from qiskit.transpiler.passes import Unroller @@ -147,9 +148,11 @@ def generate_control_flow_options_check( out = PassManager() out.append(ContainsInstruction(CONTROL_FLOW_OP_NAMES, recurse=False)) if bad_options: - out.append(Error(message), condition=_has_control_flow) + out.append(ConditionalController(Error(message), condition=_has_control_flow)) backend_control = _InvalidControlFlowForBackend(basis_gates, target) - out.append(Error(backend_control.message), condition=backend_control.condition) + out.append( + ConditionalController(Error(backend_control.message), condition=backend_control.condition) + ) return out @@ -158,7 +161,7 @@ def generate_error_on_control_flow(message): circuit.""" out = PassManager() out.append(ContainsInstruction(CONTROL_FLOW_OP_NAMES, recurse=False)) - out.append(Error(message), condition=_has_control_flow) + out.append(ConditionalController(Error(message), condition=_has_control_flow)) return out @@ -171,8 +174,8 @@ def if_has_control_flow_else(if_present, if_absent): if_absent = if_absent.to_flow_controller() out = PassManager() out.append(ContainsInstruction(CONTROL_FLOW_OP_NAMES, recurse=False)) - out.append(if_present, condition=_has_control_flow) - out.append(if_absent, condition=_without_control_flow) + out.append(ConditionalController(if_present, condition=_has_control_flow)) + out.append(ConditionalController(if_absent, condition=_without_control_flow)) return out @@ -328,32 +331,36 @@ def _swap_condition(property_set): if use_barrier_before_measurement: routing.append( - [ - BarrierBeforeFinalMeasurements( - label="qiskit.transpiler.internal.routing.protection.barrier" - ), - routing_pass, - ], - condition=_swap_condition, + ConditionalController( + [ + BarrierBeforeFinalMeasurements( + label="qiskit.transpiler.internal.routing.protection.barrier" + ), + routing_pass, + ], + condition=_swap_condition, + ) ) else: - routing.append([routing_pass], condition=_swap_condition) + routing.append(ConditionalController(routing_pass, condition=_swap_condition)) is_vf2_fully_bounded = vf2_call_limit and vf2_max_trials if (target is not None or backend_properties is not None) and is_vf2_fully_bounded: routing.append( - VF2PostLayout( - target, - coupling_map, - backend_properties, - seed_transpiler, - call_limit=vf2_call_limit, - max_trials=vf2_max_trials, - strict_direction=False, - ), - condition=_run_post_layout_condition, + ConditionalController( + VF2PostLayout( + target, + coupling_map, + backend_properties, + seed_transpiler, + call_limit=vf2_call_limit, + max_trials=vf2_max_trials, + strict_direction=False, + ), + condition=_run_post_layout_condition, + ) ) - routing.append(ApplyLayout(), condition=_apply_post_layout_condition) + routing.append(ConditionalController(ApplyLayout(), condition=_apply_post_layout_condition)) def filter_fn(node): return ( @@ -388,7 +395,12 @@ def generate_pre_op_passmanager(target=None, coupling_map=None, remove_reset_in_ def _direction_condition(property_set): return not property_set["is_direction_mapped"] - pre_opt.append([GateDirection(coupling_map, target=target)], condition=_direction_condition) + pre_opt.append( + ConditionalController( + [GateDirection(coupling_map, target=target)], + condition=_direction_condition, + ) + ) if remove_reset_in_zero: pre_opt.append(RemoveResetInZeroState()) return pre_opt @@ -554,7 +566,9 @@ def _contains_delay(property_set): scheduling.append(ContainsInstruction("delay")) scheduling.append( - TimeUnitConversion(instruction_durations, target=target), condition=_contains_delay + ConditionalController( + TimeUnitConversion(instruction_durations, target=target), condition=_contains_delay + ) ) if ( timing_constraints.granularity != 1 @@ -574,11 +588,13 @@ def _require_alignment(property_set): ) ) scheduling.append( - ConstrainedReschedule( - acquire_alignment=timing_constraints.acquire_alignment, - pulse_alignment=timing_constraints.pulse_alignment, - ), - condition=_require_alignment, + ConditionalController( + ConstrainedReschedule( + acquire_alignment=timing_constraints.acquire_alignment, + pulse_alignment=timing_constraints.pulse_alignment, + ), + condition=_require_alignment, + ) ) scheduling.append( ValidatePulseGates( diff --git a/qiskit/transpiler/propertyset.py b/qiskit/transpiler/propertyset.py deleted file mode 100644 index 6ff160b14a43..000000000000 --- a/qiskit/transpiler/propertyset.py +++ /dev/null @@ -1,19 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""A property set is maintained by the PassManager to keep information -about the current state of the circuit """ - - -# For backward compatibility -# pylint: disable=unused-import -from qiskit.passmanager import PropertySet diff --git a/qiskit/transpiler/runningpassmanager.py b/qiskit/transpiler/runningpassmanager.py deleted file mode 100644 index 548b49833265..000000000000 --- a/qiskit/transpiler/runningpassmanager.py +++ /dev/null @@ -1,174 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""RunningPassManager class for the transpiler. -This object holds the state of a pass manager during running-time.""" -from __future__ import annotations - -import inspect -import logging -from functools import wraps -from typing import Callable - -from qiskit.circuit import QuantumCircuit -from qiskit.converters import circuit_to_dag, dag_to_circuit -from qiskit.passmanager.compilation_status import PropertySet, WorkflowStatus, PassManagerState -from qiskit.passmanager.base_tasks import Task -from qiskit.passmanager.exceptions import PassManagerError -from qiskit.utils.deprecation import deprecate_func - -# pylint: disable=unused-import -from qiskit.passmanager.flow_controllers import ( - BaseController, - FlowController, - FlowControllerLinear, - # for backward compatibility - ConditionalController, - DoWhileController, -) - -from .exceptions import TranspilerError -from .layout import TranspileLayout - -logger = logging.getLogger(__name__) - - -class RunningPassManager(FlowControllerLinear): - """A RunningPassManager is a running pass manager. - - .. warning:: - - :class:`.RunningPassManager` will be deprecated in the future release. - As of Qiskit Terra 0.25 this class becomes a subclass of the flow controller - with extra methods for backward compatibility. - Relying on a subclass of the running pass manager might break your code stack. - """ - - @deprecate_func( - since="0.45.0", - additional_msg=( - "Building the pipline of the tasks is responsibility of PassManager. " - "RunningPassManager should not modify prepared pipeline at running time." - ), - ) - def append( - self, - passes: Task | list[Task], - **flow_controller_conditions, - ): - """Append a passes to the schedule of passes. - - Args: - passes: A set of passes (a pass set) to be added to schedule. A pass set is a list of - passes that are controlled by the same flow controller. If a single pass is - provided, the pass set will only have that pass a single element. - It is also possible to append a :class:`.BaseFlowController` instance and - the rest of the parameter will be ignored. - flow_controller_conditions: Dictionary of control flow plugins. - Following built-in controllers are available by default: - - * do_while: The passes repeat until the callable returns False. - * condition: The passes run only if the callable returns True. - """ - if not isinstance(passes, BaseController): - normalized_controller = passes - else: - # Backward compatibility. Will be deprecated. - normalized_controller = FlowController.controller_factory( - passes=passes, - options=self._options, - **flow_controller_conditions, - ) - super().append(normalized_controller) - - # pylint: disable=arguments-differ - @deprecate_func( - since="0.45.0", - additional_msg="Now RunningPassManager is a subclass of flow controller.", - pending=True, - ) - def run( - self, - circuit: QuantumCircuit, - output_name: str = None, - callback: Callable = None, - ) -> QuantumCircuit: - """Run all the passes on a QuantumCircuit - - Args: - circuit: Circuit to transform via all the registered passes. - output_name: The output circuit name. If not given, the same as the input circuit. - callback: A callback function that will be called after each pass execution. - - Returns: - QuantumCircuit: Transformed circuit. - """ - initial_status = WorkflowStatus() - property_set = PropertySet() - state = PassManagerState(workflow_status=initial_status, property_set=property_set) - - passmanager_ir = circuit_to_dag(circuit) - passmanager_ir, state = super().execute( - passmanager_ir=passmanager_ir, - state=state, - callback=callback, - ) - - out_circuit = dag_to_circuit(passmanager_ir, copy_operations=False) - out_circuit.name = output_name - - if state.property_set["layout"] is not None: - circuit._layout = TranspileLayout( - initial_layout=state.property_set["layout"], - input_qubit_mapping=state.property_set["original_qubit_indices"], - final_layout=state.property_set["final_layout"], - ) - circuit._clbit_write_latency = state.property_set["clbit_write_latency"] - circuit._conditional_latency = state.property_set["conditional_latency"] - - if state.property_set["node_start_time"]: - # This is dictionary keyed on the DAGOpNode, which is invalidated once - # dag is converted into circuit. So this schedule information is - # also converted into list with the same ordering with circuit.data. - topological_start_times = [] - start_times = state.property_set["node_start_time"] - for dag_node in passmanager_ir.topological_op_nodes(): - topological_start_times.append(start_times[dag_node]) - circuit._op_start_times = topological_start_times - - return circuit - - -# A temporary error handling with slight overhead at class loading. -# This method wraps all class methods to replace PassManagerError with TranspilerError. -# The pass flow controller mechanics raises PassManagerError, as it has been moved to base class. -# PassManagerError is not caught by TranspilerError due to the hierarchy. - - -def _replace_error(meth): - @wraps(meth) - def wrapper(*meth_args, **meth_kwargs): - try: - return meth(*meth_args, **meth_kwargs) - except PassManagerError as ex: - raise TranspilerError(ex.message) from ex - - return wrapper - - -for _name, _method in inspect.getmembers(RunningPassManager, predicate=inspect.isfunction): - if _name.startswith("_"): - # Ignore protected and private. - # User usually doesn't directly execute and catch error from these methods. - continue - _wrapped = _replace_error(_method) - setattr(RunningPassManager, _name, _wrapped) diff --git a/qiskit/utils/lazy_tester.py b/qiskit/utils/lazy_tester.py index 4f0d8e5a37aa..d3e5cd971185 100644 --- a/qiskit/utils/lazy_tester.py +++ b/qiskit/utils/lazy_tester.py @@ -13,14 +13,16 @@ """Lazy testers for optional features.""" import abc +import collections import contextlib import functools import importlib import subprocess import typing +import warnings from typing import Union, Iterable, Dict, Optional, Callable, Type -from qiskit.exceptions import MissingOptionalLibraryError +from qiskit.exceptions import MissingOptionalLibraryError, OptionalDependencyImportWarning from .classtools import wrap_method @@ -284,12 +286,43 @@ def __init__( super().__init__(name=name, callback=callback, install=install, msg=msg) def _is_available(self): - try: - for module, names in self._modules.items(): + failed_modules = {} + failed_names = collections.defaultdict(list) + for module, names in self._modules.items(): + try: imported = importlib.import_module(module) - for name in names: - getattr(imported, name) - except (ImportError, AttributeError): + except ModuleNotFoundError as exc: + failed_parts = exc.name.split(".") + target_parts = module.split(".") + if failed_parts == target_parts[: len(failed_parts)]: + # If the module that wasn't found is the one we were explicitly searching for + # (or one of its parents), then it's just not installed. + return False + # Otherwise, we _did_ find the module, it just didn't import, which is a problem. + failed_modules[module] = exc + continue + except ImportError as exc: + failed_modules[module] = exc + continue + for name in names: + try: + _ = getattr(imported, name) + except AttributeError: + failed_names[module].append(name) + if failed_modules or failed_names: + package_description = f"'{self._name}'" if self._name else "optional packages" + message = ( + f"While trying to import {package_description}," + " some components were located but raised other errors during import." + " You might have an incompatible version installed." + " Qiskit will continue as if the optional is not available." + ) + for module, exc in failed_modules.items(): + message += "".join(f"\n - module '{module}' failed to import with: {exc!r}") + for module, names in failed_names.items(): + attributes = f"attribute '{names[0]}'" if len(names) == 1 else f"attributes {names}" + message += "".join(f"\n - '{module}' imported, but {attributes} couldn't be found") + warnings.warn(message, category=OptionalDependencyImportWarning) return False return True diff --git a/qiskit/visualization/library.py b/qiskit/visualization/library.py new file mode 100644 index 000000000000..5832a475f8fd --- /dev/null +++ b/qiskit/visualization/library.py @@ -0,0 +1,37 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2018. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +# pylint: disable=invalid-name,no-name-in-module,ungrouped-imports + +"""A circuit library visualization""" + +from qiskit import QuantumCircuit +from qiskit.utils import optionals as _optionals + + +@_optionals.HAS_MATPLOTLIB.require_in_call +def _generate_circuit_library_visualization(circuit: QuantumCircuit): + import matplotlib.pyplot as plt + + circuit = circuit.decompose() + ops = circuit.count_ops() + num_nl = circuit.num_nonlocal_gates() + _fig, (ax0, ax1) = plt.subplots(2, 1) + circuit.draw("mpl", ax=ax0) + ax1.axis("off") + ax1.grid(visible=None) + ax1.table( + [[circuit.name], [circuit.width()], [circuit.depth()], [sum(ops.values())], [num_nl]], + rowLabels=["Circuit Name", "Width", "Depth", "Total Gates", "Non-local Gates"], + ) + plt.tight_layout() + plt.show() diff --git a/qiskit/visualization/pass_manager_visualization.py b/qiskit/visualization/pass_manager_visualization.py index 6b166ac85ffc..6c91ce9c0099 100644 --- a/qiskit/visualization/pass_manager_visualization.py +++ b/qiskit/visualization/pass_manager_visualization.py @@ -14,11 +14,15 @@ Visualization function for a pass manager. Passes are grouped based on their flow controller, and coloured based on the type of pass. """ +from __future__ import annotations + import os import inspect import tempfile from qiskit.utils import optionals as _optionals +from qiskit.passmanager.base_tasks import BaseController, GenericPass +from qiskit.passmanager.flow_controllers import FlowControllerLinear from qiskit.transpiler.basepasses import AnalysisPass, TransformationPass from .exceptions import VisualizationError @@ -74,8 +78,6 @@ def pass_manager_drawer(pass_manager, filename=None, style=None, raw=False): """ import pydot - passes = pass_manager.passes() - if not style: style = DEFAULT_STYLE @@ -89,7 +91,7 @@ def pass_manager_drawer(pass_manager, filename=None, style=None, raw=False): prev_node = None - for index, controller_group in enumerate(passes): + for index, controller_group in enumerate(pass_manager.to_flow_controller().tasks): subgraph, component_id, prev_node = draw_subgraph( controller_group, component_id, style, prev_node, index ) @@ -174,9 +176,8 @@ def staged_pass_manager_drawer(pass_manager, filename=None, style=None, raw=Fals stage = getattr(pass_manager, st) if stage is not None: - passes = stage.passes() stagegraph = pydot.Cluster(str(st), label=str(st), fontname="helvetica", labeljust="l") - for controller_group in passes: + for controller_group in stage.to_flow_controller().tasks: subgraph, component_id, prev_node = draw_subgraph( controller_group, component_id, style, prev_node, idx ) @@ -193,28 +194,65 @@ def draw_subgraph(controller_group, component_id, style, prev_node, idx): import pydot # label is the name of the flow controller parameter - label = "[{}] {}".format(idx, ", ".join(controller_group["flow_controllers"])) + label = f"[{idx}] " + if isinstance(controller_group, BaseController) and not isinstance( + controller_group, FlowControllerLinear + ): + label += f"{controller_group.__class__.__name__}" # create the subgraph for this controller subgraph = pydot.Cluster(str(component_id), label=label, fontname="helvetica", labeljust="l") component_id += 1 - for pass_ in controller_group["passes"]: - - # label is the name of the pass - node = pydot.Node( - str(component_id), - label=str(type(pass_).__name__), - color=_get_node_color(pass_, style), - shape="rectangle", - fontname="helvetica", - ) + if isinstance(controller_group, BaseController): + # Assume linear pipeline + # TODO: support pipeline branching when such controller is introduced + tasks = getattr(controller_group, "tasks", []) + elif isinstance(controller_group, GenericPass): + tasks = [controller_group] + elif isinstance(controller_group, (list, tuple)): + tasks = controller_group + else: + # Invalid data + return subgraph, component_id, prev_node + + flatten_tasks = [] + for task in tasks: + # Flatten nested linear flow controller. + # This situation often occurs in the builtin pass managers because it constructs + # some stages by appending other pass manager instance converted into a linear controller. + # Flattening inner linear controller tasks doesn't change the execution. + if isinstance(task, FlowControllerLinear): + flatten_tasks.extend(task.tasks) + else: + flatten_tasks.append(task) + + for task in flatten_tasks: + if isinstance(task, BaseController): + # Partly nested flow controller + # TODO recursively inject subgraph into subgraph + node = pydot.Node( + str(component_id), + label="Nested flow controller", + color="k", + shape="rectangle", + fontname="helvetica", + ) + else: + # label is the name of the pass + node = pydot.Node( + str(component_id), + label=str(type(task).__name__), + color=_get_node_color(task, style), + shape="rectangle", + fontname="helvetica", + ) subgraph.add_node(node) component_id += 1 # the arguments that were provided to the pass when it was created - arg_spec = inspect.getfullargspec(pass_.__init__) + arg_spec = inspect.getfullargspec(task.__init__) # 0 is the args, 1: to remove the self arg args = arg_spec[0][1:] diff --git a/releasenotes/notes/fix-delay-broadcast-e8762b01dfd7e94f.yaml b/releasenotes/notes/fix-delay-broadcast-e8762b01dfd7e94f.yaml new file mode 100644 index 000000000000..2facbc643c14 --- /dev/null +++ b/releasenotes/notes/fix-delay-broadcast-e8762b01dfd7e94f.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + The qubit-argument broadcasting of :meth:`.QuantumCircuit.delay` now correctly produces + individual :class:`~.circuit.Delay` instructions for each qubit, as intended. Previously, when + given certain iterables (such as :class:`set`\ s), it would instead silently produce an invalid + circuit that might fail in unusual locations. diff --git a/releasenotes/notes/lazy-testers-warn-on-import-errors-95a9bdaacc9c3d2b.yaml b/releasenotes/notes/lazy-testers-warn-on-import-errors-95a9bdaacc9c3d2b.yaml new file mode 100644 index 000000000000..0a3167cc1f66 --- /dev/null +++ b/releasenotes/notes/lazy-testers-warn-on-import-errors-95a9bdaacc9c3d2b.yaml @@ -0,0 +1,14 @@ +--- +features: + - | + A new warning base class, :exc:`.QiskitWarning`, was added. While Qiskit will continue to use + built-in Python warnings (such as :exc:`DeprecationWarning`) when those are most appropriate, + for cases that are more specific to Qiskit, the warnings will be subclasses of :exc:`.QiskitWarning`. + - | + :exc:`.QPYLoadingDeprecatedFeatureWarning` is now a subclass of :exc:`.QiskitWarning`. + - | + The optional-functionality testers (:mod:`qiskit.utils.optionals`) will now distinguish an + optional dependency that was completely not found (a normal situation) with one that was found, + but triggered errors during its import. In the latter case, they will now issue an + :exc:`.OptionalDependencyImportWarning` telling you what happened, since it might indicate a + failed installation or an incompatible version. diff --git a/releasenotes/notes/remove-ibmq-4bb57a04991da9af.yaml b/releasenotes/notes/remove-ibmq-4bb57a04991da9af.yaml new file mode 100644 index 000000000000..831b55ea9fdf --- /dev/null +++ b/releasenotes/notes/remove-ibmq-4bb57a04991da9af.yaml @@ -0,0 +1,34 @@ +--- +upgrade: + - | + The deprecated ``qiskit.IBMQ`` object hase been removed. This alias object + was marked as deprecated in the 0.23.0 release. This alias object lazily + redirected attribute access to ``qiskit.providers.ibmq.IBMQ``. As the + ``qiskit-ibmq-provider`` package has now been retired and superseded by + ``qiskit-ibm-provider`` package which maintains its own namespace + maintaining this alias is no longer relevant. If you + were relying on the ``qiskit.IBMQ`` alias you should migrate your usage to + the ``qiskit-ibm-provider`` package, see the + `migration guide `__ + for more details. + - | + Removed the deprecated module ``qiskit.tools.jupyter`` which previously + included jupyter magics and widgets for interactively visualizing some data + from Qiskit. This module was deprecated in Qiskit 0.46.0. Most of this + functionality was directly tied to the legacy ``qiskit-ibmq-provider`` + package and was no longer valid so the module was removed. Similar + functionality is available from the ``qiskit_ibm_provider.jupyter`` module + in the `qiskit-ibm-provider __` package. + - | + Removed the deprecated module ``qiskit.tools.monitor`` which previously + included tools for tracking :class:`.JobV1` job instances, primarily from + the legacy ``qiskit-ibm-provider`` package. This module was marked as + deprecated in Qiskit 0.46.0. It is being removed because it was directly + tied to the legacy ``qiskit-ibm-provider`` package. + - | + Removed the deprecated import path ``qiskit.test.mock`` which previously was + used to redirect imports for the mock backends to their newer location in + the :mod:`qiskit.providers.fake_provider`. This module was marked as + deprecated in Qiskit 0.37.0. If you were using this module you should update + your imports from ``qiskit.test.mock`` to + :mod:`qiskit.providers.fake_provider` instead. diff --git a/releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml b/releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml new file mode 100644 index 000000000000..be8b0ce26206 --- /dev/null +++ b/releasenotes/notes/upgrade-pass-manager-98aa64edde67b5bb.yaml @@ -0,0 +1,79 @@ +--- +upgrade: + - | + A pattern for the pass piepline construction was upgraded. + The syntactic suger shown below for instantiation of flow controller was removed. + + .. code-block:: python + + from qiskit.transpiler import PassManager + + pm = PassManager() + pm.append(my_pass, condition=condition_callable, do_while=do_while_callable) + + Instead of using this keyword argument pattern, you should explicitly instantiate the + flow controller. + + .. code-block:: python + + from qiskit.passmanager.flow_controllers import ConditionalController, DoWhileController + + pm = PassManager() + pm.append( + ConditionalController( + DoWhileController(my_pass, do_while=do_while_callable), + condition=condition_callable, + ) + ) + + Note that you can manage the pecking order of controllers when you want to nest them, + which was not possible with keyword arguments. + You can also build the pipeline with the constructor of the pass manager like below + because there is no reason to call the append method now. + + .. code-block:: python + + pm = PassManager( + ConditionalController( + DoWhileController(my_pass, do_while=do_while_callable), + condition=condition_callable, + ) + ) + + - | + The append method of builtin flow controllers was removed. This includes + + * :meth:`.ConditionalController.append` + * :meth:`.DoWhileController.append` + * :meth:`.FlowControllerLinear.append` + + The task pipeline in a flow controller is frozen, and it must be passed + when the controller instance is created. + + - | + Removed methods :meth:`qiskit.transpiler.PassManager.passes` and + :meth:`qiskit.transpiler.StagedPassManager.passes` that generates + a representation of inset passes in the form of list of dictionary, + however, this format doesn't efficiently represent more complicated pass pipeline, + which may include conditional branching and nested conditions. + Instead of using this representation, please use following pattern + + .. code-block:: python + + pm = PassManager(...) + pm.to_flow_controller().tasks + + This directly returns a linearized base task instances in tuple format. + + - | + The max_iteration argument was removed from :meth:`qiskit.transpiler.PassManager.append` + and :meth:`qiskit.transpiler.PassManager.replace`. + + - | + The following legacy classes were removed from the pass manager and transpiler module. + + * :class:`qiskit.passmanager.flow_controllers.FlowController` + * :class:`qiskit.transpiler.fencedobjs.FencedObject` + * :class:`qiskit.transpiler.fencedobjs.FencedPropertySet` + * :class:`qiskit.transpiler.fencedobjs.FencedDAGCircuit` + * :class:`qiskit.transpiler.runningpassmanager.RunningPassManager` diff --git a/requirements-optional.txt b/requirements-optional.txt index 0e94040e69c1..36985cdd7cd9 100644 --- a/requirements-optional.txt +++ b/requirements-optional.txt @@ -8,12 +8,9 @@ # Test-runner enhancements. fixtures testtools -jupyter # Interactivity. -ipykernel ipython -ipywidgets>=7.3.0 matplotlib>=3.3 pillow>=4.2.1 pydot diff --git a/test/python/circuit/test_delay.py b/test/python/circuit/test_delay.py index e4b0c0193308..f7b1d3f770d8 100644 --- a/test/python/circuit/test_delay.py +++ b/test/python/circuit/test_delay.py @@ -82,6 +82,17 @@ def test_add_delay_on_multiple_qubits_to_circuit(self): expected.delay(300, 2) self.assertEqual(qc, expected) + def test_delay_set(self): + """Test that a set argument to `delay` works.""" + qc = QuantumCircuit(5) + qc.delay(8, {0, 1, 3, 4}) + expected = QuantumCircuit(5) + expected.delay(8, 0) + expected.delay(8, 1) + expected.delay(8, 3) + expected.delay(8, 4) + self.assertEqual(qc, expected) + def test_to_matrix_return_identity_matrix(self): actual = np.array(Delay(100)) expected = np.array([[1, 0], [0, 1]], dtype=complex) diff --git a/test/python/mock/test_mock_module_deprecation.py b/test/python/mock/test_mock_module_deprecation.py deleted file mode 100644 index d2355c84717b..000000000000 --- a/test/python/mock/test_mock_module_deprecation.py +++ /dev/null @@ -1,28 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2022. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Test for deprecation of qiskit.test.mock module.""" -from qiskit.test import QiskitTestCase - - -class MockModuleDeprecationTest(QiskitTestCase): - """Test for deprecation of qiskit.test.mock module.""" - - def test_deprecated_mock_module(self): - """Test that the mock module is deprecated.""" - # pylint: disable=unused-import,no-name-in-module - with self.assertWarns(DeprecationWarning): - from qiskit.test.mock import FakeWashington - with self.assertWarns(DeprecationWarning): - from qiskit.test.mock.backends import FakeWashington - with self.assertWarns(DeprecationWarning): - from qiskit.test.mock.backends.washington import FakeWashington diff --git a/test/python/notebooks/test_backend_tools.ipynb b/test/python/notebooks/test_backend_tools.ipynb deleted file mode 100644 index d2d892b9cd56..000000000000 --- a/test/python/notebooks/test_backend_tools.ipynb +++ /dev/null @@ -1,156 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-09T09:29:08.426863Z", - "start_time": "2019-08-09T09:29:08.423361Z" - } - }, - "outputs": [], - "source": [ - "import os\n", - "import sys\n", - "\n", - "cwd = os.getcwd()\n", - "qiskit_dir = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(cwd))))\n", - "sys.path.append(qiskit_dir)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-09T09:29:08.669360Z", - "start_time": "2019-08-09T09:29:08.612812Z" - } - }, - "outputs": [], - "source": [ - "from qiskit import IBMQ\n", - "from qiskit.tools.jupyter import *" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-09T09:29:11.253688Z", - "start_time": "2019-08-09T09:29:08.791274Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "IBMQ.load_account()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-09T09:29:25.505599Z", - "start_time": "2019-08-09T09:29:11.266347Z" - } - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "99942fff1dc24cdf9a105c018f6a1260", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(HTML(value=\"

=3.8 on osx and windows", - ) - def test_jupyter_jobs_pbars(self): - """Test Jupyter progress bars and job status functionality""" - self._execute_notebook(os.path.join(self.notebook_dir, "test_pbar_status.ipynb")) - - @unittest.skipIf(not optionals.HAS_MATPLOTLIB, "matplotlib not available.") - @slow_test - def test_backend_tools(self): - """Test Jupyter backend tools.""" - self._execute_notebook(os.path.join(self.notebook_dir, "test_backend_tools.ipynb")) - - -if __name__ == "__main__": - unittest.main(verbosity=2) diff --git a/test/python/tools/monitor/__init__.py b/test/python/tools/monitor/__init__.py deleted file mode 100644 index 146d5c0e0b91..000000000000 --- a/test/python/tools/monitor/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for backend monitor-related functionality.""" diff --git a/test/python/tools/monitor/test_backend_monitor.py b/test/python/tools/monitor/test_backend_monitor.py deleted file mode 100644 index a2c4802802e6..000000000000 --- a/test/python/tools/monitor/test_backend_monitor.py +++ /dev/null @@ -1,103 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 2018. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -"""Tests for the wrapper functionality.""" - -import sys -import unittest -from unittest.mock import patch -from unittest.mock import MagicMock -from io import StringIO - -import qiskit -from qiskit import providers -from qiskit.tools.monitor import backend_overview, backend_monitor -from qiskit.test import QiskitTestCase -from qiskit.providers.fake_provider import FakeProviderFactory, FakeBackend, FakeVigo - - -class TestBackendOverview(QiskitTestCase): - """Tools test case.""" - - def _restore_ibmq(self): - if not self.import_error: - qiskit.IBMQ = self.ibmq_back - else: - del qiskit.IBMQ - if self.prov_backup: - providers.ibmq = self.prov_backup - else: - del providers.ibmq - - def _restore_ibmq_mod(self): - if self.ibmq_module_backup is not None: - sys.modules["qiskit.providers.ibmq"] = self.ibmq_module_backup - else: - sys.modules.pop("qiskit.providers.ibmq") - - def setUp(self): - super().setUp() - ibmq_mock = MagicMock() - ibmq_mock.IBMQBackend = FakeBackend - if "qiskit.providers.ibmq" in sys.modules: - self.ibmq_module_backup = sys.modules["qiskit.providers.ibmq"] - else: - self.ibmq_module_backup = None - sys.modules["qiskit.providers.ibmq"] = ibmq_mock - self.addCleanup(self._restore_ibmq_mod) - - if hasattr(qiskit, "IBMQ"): - self.import_error = False - else: - self.import_error = True - qiskit.IBMQ = None - self.ibmq_back = qiskit.IBMQ - qiskit.IBMQ = FakeProviderFactory() - self.addCleanup(self._restore_ibmq) - if hasattr(providers, "ibmq"): - self.prov_backup = providers.ibmq - else: - self.prov_backup = None - providers.ibmq = MagicMock() - - @patch("qiskit.tools.monitor.overview.get_unique_backends", return_value=[FakeVigo()]) - def test_backend_overview(self, _): - """Test backend_overview""" - with patch("sys.stdout", new=StringIO()) as fake_stdout: - backend_overview() - stdout = fake_stdout.getvalue() - self.assertIn("Operational:", stdout) - self.assertIn("Avg. T1:", stdout) - self.assertIn("Num. Qubits:", stdout) - - @patch("qiskit.tools.monitor.overview.get_unique_backends", return_value=[FakeVigo()]) - def test_backend_monitor(self, _): - """Test backend_monitor""" - for back in [FakeVigo()]: - if not back.configuration().simulator: - backend = back - break - with patch("sys.stdout", new=StringIO()) as fake_stdout: - backend_monitor(backend) - - stdout = fake_stdout.getvalue() - self.assertIn("Configuration", stdout) - self.assertIn("Qubits [Name / Freq / T1 / T2 / ", stdout) - for gate in backend.properties().gates: - if gate.gate not in ["id"] and len(gate.qubits) == 1: - self.assertIn(gate.gate.upper() + " err", stdout) - self.assertIn("Readout err", stdout) - self.assertIn("Multi-Qubit Gates [Name / Type / Gate Error]", stdout) - - -if __name__ == "__main__": - unittest.main(verbosity=2) diff --git a/test/python/transpiler/test_commutative_cancellation.py b/test/python/transpiler/test_commutative_cancellation.py index 577ae886afc7..31931a7fdb4c 100644 --- a/test/python/transpiler/test_commutative_cancellation.py +++ b/test/python/transpiler/test_commutative_cancellation.py @@ -19,6 +19,7 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.circuit.library import U1Gate, RZGate, PhaseGate, CXGate, SXGate from qiskit.circuit.parameter import Parameter +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler.target import Target from qiskit.transpiler import PassManager, PropertySet from qiskit.transpiler.passes import CommutationAnalysis, CommutativeCancellation, FixedPoint, Size @@ -136,8 +137,15 @@ def test_consecutive_cnots2(self): passmanager = PassManager() passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) @@ -410,8 +418,15 @@ def test_commutative_circuit3(self): passmanager = PassManager() passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) @@ -453,8 +468,15 @@ def test_cnot_cascade(self): passmanager = PassManager() # passmanager.append(CommutativeCancellation()) passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) @@ -509,8 +531,15 @@ def test_cnot_cascade1(self): passmanager = PassManager() # passmanager.append(CommutativeCancellation()) passmanager.append( - [CommutationAnalysis(), CommutativeCancellation(), Size(), FixedPoint("size")], - do_while=lambda property_set: not property_set["size_fixed_point"], + DoWhileController( + [ + CommutationAnalysis(), + CommutativeCancellation(), + Size(), + FixedPoint("size"), + ], + do_while=lambda property_set: not property_set["size_fixed_point"], + ) ) new_circuit = passmanager.run(circuit) expected = QuantumCircuit(qr) diff --git a/test/python/transpiler/test_gates_in_basis_pass.py b/test/python/transpiler/test_gates_in_basis_pass.py index e181423a00ae..4092b312b337 100644 --- a/test/python/transpiler/test_gates_in_basis_pass.py +++ b/test/python/transpiler/test_gates_in_basis_pass.py @@ -16,6 +16,7 @@ from qiskit.circuit.library import HGate, CXGate, UGate, XGate, ZGate from qiskit.circuit.measure import Measure from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary +from qiskit.passmanager.flow_controllers import ConditionalController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import BasisTranslator from qiskit.transpiler.passes import GatesInBasis @@ -86,8 +87,10 @@ def test_all_gates_in_basis_after_translation(self): pm = PassManager() pm.append(analysis_pass) pm.append( - BasisTranslator(SessionEquivalenceLibrary, basis_gates), - condition=lambda property_set: not property_set["all_gates_in_basis"], + ConditionalController( + BasisTranslator(SessionEquivalenceLibrary, basis_gates), + condition=lambda property_set: not property_set["all_gates_in_basis"], + ) ) pm.append(analysis_pass) pm.run(circuit) @@ -200,8 +203,10 @@ def test_all_gates_in_basis_after_translation_with_target(self): pm = PassManager() pm.append(analysis_pass) pm.append( - BasisTranslator(SessionEquivalenceLibrary, basis_gates, target=target), - condition=lambda property_set: not property_set["all_gates_in_basis"], + ConditionalController( + BasisTranslator(SessionEquivalenceLibrary, basis_gates, target=target), + condition=lambda property_set: not property_set["all_gates_in_basis"], + ) ) pm.append(analysis_pass) pm.run(circuit) diff --git a/test/python/transpiler/test_optimize_swap_before_measure.py b/test/python/transpiler/test_optimize_swap_before_measure.py index 75b0079c6650..258796e56929 100644 --- a/test/python/transpiler/test_optimize_swap_before_measure.py +++ b/test/python/transpiler/test_optimize_swap_before_measure.py @@ -15,6 +15,7 @@ import unittest from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import OptimizeSwapBeforeMeasure, DAGFixedPoint from qiskit.converters import circuit_to_dag @@ -311,8 +312,10 @@ def test_optimize_undone_swap(self): pass_manager = PassManager() pass_manager.append( - [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) @@ -340,8 +343,10 @@ def test_optimize_overlap_swap(self): pass_manager = PassManager() pass_manager.append( - [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [OptimizeSwapBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) diff --git a/test/python/transpiler/test_pass_scheduler.py b/test/python/transpiler/test_pass_scheduler.py index a52ad8b1ece2..263db4f64914 100644 --- a/test/python/transpiler/test_pass_scheduler.py +++ b/test/python/transpiler/test_pass_scheduler.py @@ -19,11 +19,7 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.transpiler import PassManager, TranspilerError -from qiskit.transpiler.runningpassmanager import ( - DoWhileController, - ConditionalController, - FlowController, -) +from qiskit.passmanager.flow_controllers import DoWhileController, ConditionalController from qiskit.test import QiskitTestCase from ._dummy_passes import ( PassA_TP_NR_NP, @@ -136,7 +132,9 @@ def test_conditional_passes_true(self): """A pass set with a conditional parameter. The callable is True.""" self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ) ) self.assertScheduler( self.circuit, @@ -171,7 +169,9 @@ def test_conditional_passes_false(self): """A pass set with a conditional parameter. The callable is False.""" self.passmanager.append(PassE_AP_NR_NP(False)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ) ) self.assertScheduler( self.circuit, @@ -183,9 +183,17 @@ def test_conditional_and_loop(self): """Run a conditional first, then a loop.""" self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: property_set["property"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: property_set["property"], + ) ) self.assertScheduler( self.circuit, @@ -240,15 +248,19 @@ def test_conditional_and_loop(self): def test_loop_and_conditional(self): """Run a loop first, then a conditional.""" - with self.assertWarns(DeprecationWarning): - FlowController.remove_flow_controller("condition") - FlowController.add_flow_controller("condition", ConditionalController) - self.passmanager.append(PassK_check_fixed_point_property()) self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: not property_set["property_fixed_point"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: not property_set["property_fixed_point"], + ) ) self.assertScheduler( self.circuit, @@ -380,8 +392,14 @@ def test_pass_no_return(self): def test_fixed_point_pass(self): """A pass set with a do_while parameter that checks for a fixed point.""" self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ) ) self.assertScheduler( self.circuit, @@ -436,7 +454,11 @@ def test_fixed_point_fc(self): """A fixed point scheduler with flow control.""" self.passmanager.append( DoWhileController( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], do_while=lambda property_set: not property_set["property_fixed_point"], ) ) @@ -492,9 +514,11 @@ def test_fixed_point_pass_max_iteration(self): """A pass set with a do_while parameter that checks that the max_iteration is raised.""" self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - max_iteration=2, + DoWhileController( + [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], + do_while=lambda property_set: not property_set["property_fixed_point"], + options={"max_iteration": 2}, + ), ) self.assertSchedulerRaises( self.circuit, @@ -535,16 +559,18 @@ def test_fresh_initial_state(self): def test_nested_conditional_in_loop(self): """Run a loop with a nested conditional.""" - nested_conditional = [ - ConditionalController( - [PassA_TP_NR_NP()], condition=lambda property_set: property_set["property"] >= 5 - ) - ] + nested_conditional = ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] >= 5 + ) self.passmanager.append( - [PassK_check_fixed_point_property()] - + nested_conditional - + [PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], + DoWhileController( + [ + PassK_check_fixed_point_property(), + nested_conditional, + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ) ) expected = [ "run analysis pass PassG_calculates_dag_property", @@ -589,21 +615,6 @@ def test_nested_conditional_in_loop(self): self.assertScheduler(self.circuit, self.passmanager, expected) -class DoXTimesController(FlowController): - """A control-flow plugin for running a set of passes an X amount of times.""" - - def __init__(self, passes, options, do_x_times, **_): - super().__init__(options) - self.passes = passes - self.do_x_times = do_x_times - - # pylint: disable=missing-function-docstring - def iter_tasks(self, metadata): - for _ in range(self.do_x_times(metadata.property_set)): - for pass_ in self.passes: - metadata = yield pass_ - - class TestControlFlowPlugin(SchedulerTestCase): """Testing the control flow plugin system.""" @@ -612,58 +623,6 @@ def setUp(self): self.passmanager = PassManager() self.circuit = QuantumCircuit(QuantumRegister(1)) - def test_control_flow_plugin(self): - """Adds a control flow plugin with a single parameter and runs it.""" - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_x_times", DoXTimesController) - self.passmanager.append([PassB_TP_RA_PA(), PassC_TP_RA_PA()], do_x_times=lambda x: 3) - self.assertScheduler( - self.circuit, - self.passmanager, - [ - "run transformation pass PassA_TP_NR_NP", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - ], - ) - - def test_callable_control_flow_plugin(self): - """Removes do_while, then adds it back. Checks max_iteration still working.""" - controllers_length = len(FlowController.registered_controllers) - with self.assertWarns(DeprecationWarning): - FlowController.remove_flow_controller("do_while") - self.assertEqual(controllers_length - 1, len(FlowController.registered_controllers)) - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_while", DoWhileController) - self.assertEqual(controllers_length, len(FlowController.registered_controllers)) - self.passmanager.append( - [PassB_TP_RA_PA(), PassC_TP_RA_PA()], - do_while=lambda property_set: True, - max_iteration=2, - ) - self.assertSchedulerRaises( - self.circuit, - self.passmanager, - [ - "run transformation pass PassA_TP_NR_NP", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - "run transformation pass PassB_TP_RA_PA", - "run transformation pass PassC_TP_RA_PA", - ], - TranspilerError, - ) - - def test_remove_nonexistent_plugin(self): - """Tries to remove a plugin that does not exist.""" - with self.assertRaises(KeyError): - with self.assertWarns(DeprecationWarning): - FlowController.remove_flow_controller("foo") - class TestDumpPasses(SchedulerTestCase): """Testing the passes method.""" @@ -673,12 +632,8 @@ def test_passes(self): passmanager = PassManager() passmanager.append(PassC_TP_RA_PA()) passmanager.append(PassB_TP_RA_PA()) - - expected = [ - {"flow_controllers": {}, "passes": [PassC_TP_RA_PA()]}, - {"flow_controllers": {}, "passes": [PassB_TP_RA_PA()]}, - ] - self.assertEqual(expected, passmanager.passes()) + expected = PassC_TP_RA_PA(), PassB_TP_RA_PA() + self.assertEqual(expected, passmanager.to_flow_controller().tasks) def test_passes_in_linear(self): """Dump passes in the same FlowControllerLinear""" @@ -690,54 +645,34 @@ def test_passes_in_linear(self): PassB_TP_RA_PA(), ] ) - - expected = [ - { - "flow_controllers": {}, - "passes": [ - PassC_TP_RA_PA(), - PassB_TP_RA_PA(), - PassD_TP_NR_NP(argument1=[1, 2]), - PassB_TP_RA_PA(), - ], - } - ] - self.assertEqual(expected, passmanager.passes()) - - def test_control_flow_plugin(self): - """Dump passes in a custom flow controller.""" - passmanager = PassManager() - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_x_times", DoXTimesController) - passmanager.append([PassB_TP_RA_PA(), PassC_TP_RA_PA()], do_x_times=lambda x: 3) - - expected = [ - {"passes": [PassB_TP_RA_PA(), PassC_TP_RA_PA()], "flow_controllers": {"do_x_times"}} - ] - self.assertEqual(expected, passmanager.passes()) + expected = ( + PassC_TP_RA_PA(), + PassB_TP_RA_PA(), + PassD_TP_NR_NP(argument1=[1, 2]), + PassB_TP_RA_PA(), + ) + self.assertEqual(expected, passmanager.to_flow_controller().tasks) def test_conditional_and_loop(self): """Dump passes with a conditional and a loop.""" - passmanager = PassManager() - passmanager.append(PassE_AP_NR_NP(True)) - passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: property_set["property_fixed_point"], - ) - - expected = [ - {"passes": [PassE_AP_NR_NP(True)], "flow_controllers": {}}, - { - "passes": [ + nested_controller = ConditionalController( + DoWhileController( + [ PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property(), ], - "flow_controllers": {"condition", "do_while"}, - }, - ] - self.assertEqual(expected, passmanager.passes()) + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: property_set["property_fixed_point"], + ) + + passmanager = PassManager() + passmanager.append(PassE_AP_NR_NP(True)) + passmanager.append(nested_controller) + + expected = (PassE_AP_NR_NP(True), nested_controller) + self.assertEqual(expected, passmanager.to_flow_controller().tasks) class StreamHandlerRaiseException(StreamHandler): @@ -801,33 +736,22 @@ def test_passes_in_linear(self): ], ) - def test_control_flow_plugin(self): - """Dump passes in a custom flow controller.""" - passmanager = PassManager() - with self.assertWarns(DeprecationWarning): - FlowController.add_flow_controller("do_x_times", DoXTimesController) - passmanager.append([PassB_TP_RA_PA(), PassC_TP_RA_PA()], do_x_times=lambda x: 3) - self.assertPassLog( - passmanager, - [ - "PassA_TP_NR_NP", - "PassB_TP_RA_PA", - "PassC_TP_RA_PA", - "PassB_TP_RA_PA", - "PassC_TP_RA_PA", - "PassB_TP_RA_PA", - "PassC_TP_RA_PA", - ], - ) - def test_conditional_and_loop(self): """Dump passes with a conditional and a loop""" passmanager = PassManager() passmanager.append(PassE_AP_NR_NP(True)) passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], - condition=lambda property_set: property_set["property_fixed_point"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: property_set["property_fixed_point"], + ) ) self.assertPassLog(passmanager, ["PassE_AP_NR_NP"]) @@ -858,7 +782,9 @@ def test_conditional_twice(self): """Run a conditional twice.""" self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ) ) expected = [ @@ -873,8 +799,14 @@ def test_conditional_twice(self): def test_fixed_point_twice(self): """A fixed point scheduler, twice.""" self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - do_while=lambda property_set: not property_set["property_fixed_point"], + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ) ) expected = [ @@ -1015,7 +947,10 @@ def test_replace_with_conditional(self): self.passmanager.append(PassB_TP_RA_PA()) self.passmanager.replace( - 1, PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + 1, + ConditionalController( + PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ), ) expected = ["run analysis pass PassE_AP_NR_NP", "set property as False"] @@ -1071,9 +1006,17 @@ def test_accessing_passmanager_by_index_with_condition(self): """test accessing PassManager's conditioned passes by index""" self.passmanager.append(PassF_reduce_dag_property()) self.passmanager.append( - [PassK_check_fixed_point_property(), PassA_TP_NR_NP(), PassF_reduce_dag_property()], - condition=lambda property_set: True, - do_while=lambda property_set: not property_set["property_fixed_point"], + ConditionalController( + DoWhileController( + [ + PassK_check_fixed_point_property(), + PassA_TP_NR_NP(), + PassF_reduce_dag_property(), + ], + do_while=lambda property_set: not property_set["property_fixed_point"], + ), + condition=lambda property_set: True, + ) ) new_passmanager = self.passmanager[1] @@ -1145,7 +1088,10 @@ def test_accessing_passmanager_by_range_with_condition(self): self.passmanager.append(PassB_TP_RA_PA()) self.passmanager.append(PassE_AP_NR_NP(True)) self.passmanager.append( - PassA_TP_NR_NP(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassA_TP_NR_NP(), + condition=lambda property_set: property_set["property"], + ) ) self.passmanager.append(PassB_TP_RA_PA()) @@ -1194,7 +1140,9 @@ def test_concatenating_passmanagers_with_condition(self): self.passmanager1.append(PassE_AP_NR_NP(True)) self.passmanager1.append(PassB_TP_RA_PA()) self.passmanager2.append( - PassC_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassC_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ) ) self.passmanager2.append(PassB_TP_RA_PA()) @@ -1247,7 +1195,9 @@ def test_adding_list_of_passes_to_passmanager_with_condition(self): """test adding a list of passes to a PassManager that have conditions""" self.passmanager1.append(PassE_AP_NR_NP(False)) self.passmanager1.append( - PassB_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ConditionalController( + PassB_TP_RA_PA(), condition=lambda property_set: property_set["property"] + ) ) self.passmanager1 += PassC_TP_RA_PA() diff --git a/test/python/transpiler/test_passmanager.py b/test/python/transpiler/test_passmanager.py index 3ed34083ee31..181dace29acf 100644 --- a/test/python/transpiler/test_passmanager.py +++ b/test/python/transpiler/test_passmanager.py @@ -21,7 +21,11 @@ from qiskit import QuantumRegister, QuantumCircuit from qiskit.circuit.library import U2Gate from qiskit.converters import circuit_to_dag -from qiskit.passmanager.flow_controllers import FlowControllerLinear +from qiskit.passmanager.flow_controllers import ( + FlowControllerLinear, + ConditionalController, + DoWhileController, +) from qiskit.transpiler import PassManager, PropertySet, TransformationPass from qiskit.transpiler.passes import CommutativeCancellation from qiskit.transpiler.passes import Optimize1qGates, Unroller @@ -144,20 +148,20 @@ def condition(_): def make_inner(prefix): inner = PassManager() inner.append(DummyPass(f"{prefix} 1")) - inner.append(DummyPass(f"{prefix} 2"), condition=lambda _: False) - inner.append(DummyPass(f"{prefix} 3"), condition=lambda _: True) - inner.append(DummyPass(f"{prefix} 4"), do_while=repeat(1)) + inner.append(ConditionalController(DummyPass(f"{prefix} 2"), condition=lambda _: False)) + inner.append(ConditionalController(DummyPass(f"{prefix} 3"), condition=lambda _: True)) + inner.append(DoWhileController(DummyPass(f"{prefix} 4"), do_while=repeat(1))) return inner.to_flow_controller() self.assertIsInstance(make_inner("test"), FlowControllerLinear) outer = PassManager() outer.append(make_inner("first")) - outer.append(make_inner("second"), condition=lambda _: False) + outer.append(ConditionalController(make_inner("second"), condition=lambda _: False)) # The intent of this `condition=repeat(1)` is to ensure that the outer condition is only # checked once and not flattened into the inner controllers; an inner pass invalidating the # condition should not affect subsequent passes once the initial condition was met. - outer.append(make_inner("third"), condition=repeat(1)) + outer.append(ConditionalController(make_inner("third"), condition=repeat(1))) calls = [] diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index 1e737abdcfc2..190227f99b11 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -1296,13 +1296,11 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] + x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks ] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) @@ -1330,13 +1328,11 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] + x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks ] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) @@ -1364,13 +1360,11 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] + x.__class__.__name__ for x in pm.translation.to_flow_controller().tasks ] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) @@ -1398,14 +1392,10 @@ def get_translation_stage_plugin(self): pm = generate_preset_pass_manager(optimization_level, backend=target) self.assertIsInstance(pm, PassManager) - pass_list = [y.__class__.__name__ for x in pm.passes() for y in x["passes"]] + pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("PadDynamicalDecoupling", pass_list) self.assertIn("ALAPScheduleAnalysis", pass_list) - post_translation_pass_list = [ - y.__class__.__name__ - for x in pm.translation.passes() # pylint: disable=no-member - for y in x["passes"] - ] + post_translation_pass_list = [x.__class__.__name__ for x in pm.to_flow_controller().tasks] self.assertIn("RemoveResetInZeroState", post_translation_pass_list) def test_generate_preset_pass_manager_with_list_coupling_map(self): diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index d58bc47bf57d..b68eca204bba 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -17,6 +17,7 @@ from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister from qiskit.circuit.library import U1Gate, CU1Gate +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure, DAGFixedPoint from qiskit.converters import circuit_to_dag @@ -450,8 +451,10 @@ def test_optimize_rz_z(self): pass_manager = PassManager() pass_manager.append( - [RemoveDiagonalGatesBeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [RemoveDiagonalGatesBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) diff --git a/test/python/transpiler/test_remove_reset_in_zero_state.py b/test/python/transpiler/test_remove_reset_in_zero_state.py index 64767d1e07ec..ebcd2087c0ec 100644 --- a/test/python/transpiler/test_remove_reset_in_zero_state.py +++ b/test/python/transpiler/test_remove_reset_in_zero_state.py @@ -15,6 +15,7 @@ import unittest from qiskit import QuantumRegister, QuantumCircuit +from qiskit.passmanager.flow_controllers import DoWhileController from qiskit.transpiler import PassManager from qiskit.transpiler.passes import RemoveResetInZeroState, DAGFixedPoint from qiskit.converters import circuit_to_dag @@ -94,8 +95,10 @@ def test_two_resets(self): pass_manager = PassManager() pass_manager.append( - [RemoveResetInZeroState(), DAGFixedPoint()], - do_while=lambda property_set: not property_set["dag_fixed_point"], + DoWhileController( + [RemoveResetInZeroState(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) ) after = pass_manager.run(circuit) diff --git a/test/python/transpiler/test_staged_passmanager.py b/test/python/transpiler/test_staged_passmanager.py index 677426bad3d6..33bec0f4f913 100644 --- a/test/python/transpiler/test_staged_passmanager.py +++ b/test/python/transpiler/test_staged_passmanager.py @@ -37,7 +37,7 @@ def test_default_stages(self): scheduling=PassManager([Depth()]), ) self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Optimize1qGates", "Unroller", "Depth"], ) @@ -45,14 +45,14 @@ def test_inplace_edit(self): spm = StagedPassManager(stages=["single_stage"]) spm.single_stage = PassManager([Optimize1qGates(), Depth()]) self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Optimize1qGates", "Depth"], ) with self.assertWarns(DeprecationWarning): spm.single_stage.append(Unroller(["u"])) spm.single_stage.append(Depth()) self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Optimize1qGates", "Depth", "Unroller", "Depth"], ) @@ -62,8 +62,9 @@ def test_invalid_stage(self): def test_pre_phase_is_valid_stage(self): spm = StagedPassManager(stages=["init"], pre_init=PassManager([Depth()])) + self.assertEqual( - [x.__class__.__name__ for passes in spm.passes() for x in passes["passes"]], + [x.__class__.__name__ for x in spm.to_flow_controller().tasks], ["Depth"], ) @@ -110,16 +111,16 @@ def test_repeated_stages(self): spm = StagedPassManager( stages, pre_alpha=pre_alpha, alpha=alpha, post_alpha=post_alpha, omega=omega ) - passes = [ - *pre_alpha.passes(), - *alpha.passes(), - *post_alpha.passes(), - *omega.passes(), - *pre_alpha.passes(), - *alpha.passes(), - *post_alpha.passes(), - ] - self.assertEqual(spm.passes(), passes) + passes = ( + *pre_alpha.to_flow_controller().tasks, + *alpha.to_flow_controller().tasks, + *post_alpha.to_flow_controller().tasks, + *omega.to_flow_controller().tasks, + *pre_alpha.to_flow_controller().tasks, + *alpha.to_flow_controller().tasks, + *post_alpha.to_flow_controller().tasks, + ) + self.assertEqual(spm.to_flow_controller().tasks, passes) def test_edit_stages(self): spm = StagedPassManager() diff --git a/test/python/utils/test_lazy_loaders.py b/test/python/utils/test_lazy_loaders.py index 614bea15f04d..7a8c16eda2fe 100644 --- a/test/python/utils/test_lazy_loaders.py +++ b/test/python/utils/test_lazy_loaders.py @@ -12,12 +12,17 @@ """Tests for the lazy loaders.""" +from __future__ import annotations + +import importlib.abc +import importlib.util import sys +import warnings from unittest import mock import ddt -from qiskit.exceptions import MissingOptionalLibraryError +from qiskit.exceptions import MissingOptionalLibraryError, OptionalDependencyImportWarning from qiskit.test import QiskitTestCase from qiskit.utils import LazyImportTester, LazySubprocessTester @@ -49,6 +54,29 @@ def mock_availability_test(feature): return mock.patch.object(type(feature), "_is_available", wraps=feature._is_available) +def patch_imports(mapping: dict[str, importlib.abc.Loader]): + """Patch the import system so that the given named modules will skip the regular search system + and instead be loaded by the given loaders. + + Already imported modules will not be affected; this should use uniquely named modules.""" + + class OverrideLoaders(importlib.abc.MetaPathFinder): + """A metapath finder that will simply return an explicit loader for specific modules.""" + + def __init__(self, mapping: dict[str, importlib.abc.Loader]): + self.mapping = mapping + + def find_spec(self, fullname, path, target=None): + """Implementation of the abstract (but undefined) method.""" + del path, target # ABC parameters we don't need. + if (loader := self.mapping.get(fullname)) is not None: + return importlib.util.spec_from_loader(fullname, loader) + return None + + new_path = [OverrideLoaders(mapping)] + sys.meta_path + return mock.patch.object(sys, "meta_path", new_path) + + @ddt.ddt class TestLazyDependencyTester(QiskitTestCase): """Tests for the lazy loaders. Within this class, we parameterise the test cases with @@ -71,6 +99,23 @@ def test_evaluates_correctly_false(self, test_generator): if test_generator(): self.fail("did not evaluate false") + def test_submodule_import_detects_false_correctly(self): + """Test that a lazy import of a submodule where the parent is not available still generates + a silent failure.""" + + # The idea here is that the base package is what will fail the import, and the corresponding + # `ImportError.name` won't be the same as the full path we were trying to import. We want + # to make sure that the "was it found and failed to import?" handling is correct in this + # case. + def checker(): + return LazyImportTester("_qiskit_module_does_not_exist_.submodule") + + # Just in case something else is allowing the warnings, but they should be forbidden by + # default. + with warnings.catch_warnings(record=True) as log: + self.assertFalse(checker()) + self.assertEqual(log, []) + @ddt.data(available_importer, available_process, unavailable_importer, unavailable_process) def test_check_occurs_once(self, test_generator): """Check that the test of availability is only performed once.""" @@ -324,6 +369,59 @@ class Module: vars(type(mock_module))[attribute].assert_called() vars(type(mock_module))["unaccessed_attribute"].assert_not_called() + def test_warns_on_import_error(self): + """Check that the module raising an `ImportError` other than being not found is warned + against.""" + + # pylint: disable=missing-class-docstring,missing-function-docstring,abstract-method + + class RaisesImportErrorOnLoad(importlib.abc.Loader): + def __init__(self, name): + self.name = name + + def create_module(self, spec): + raise ImportError("sentinel import failure", name=self.name) + + def exec_module(self, module): + pass + + dummy = f"{__name__}_{type(self).__name__}_test_warns_on_import_error".replace(".", "_") + tester = LazyImportTester(dummy) + with patch_imports({dummy: RaisesImportErrorOnLoad(dummy)}): + with self.assertWarnsRegex( + OptionalDependencyImportWarning, + rf"module '{dummy}' failed to import with: .*sentinel import failure.*", + ): + self.assertFalse(tester) + + def test_warns_on_internal_not_found_error(self): + """Check that the module raising an `ModuleNotFoundError` for some module other than itself + (such as a module trying to import parts of Terra that don't exist any more) is caught and + warned against, rather than silently caught as an expected `ModuleNotFoundError`.""" + + # pylint: disable=missing-class-docstring,missing-function-docstring,abstract-method + + class ImportsBadModule(importlib.abc.Loader): + def create_module(self, spec): + # Doesn't matter what, we just want to return any module object; we're going to + # raise an error during "execution" of the module. + return sys + + def exec_module(self, module): + del module # ABC parameter we don't care about. + import __qiskit__some_module_that_does_not_exist + + dummy = f"{__name__}_{type(self).__name__}_test_warns_on_internal_not_found_error".replace( + ".", "_" + ) + tester = LazyImportTester(dummy) + with patch_imports({dummy: ImportsBadModule()}): + with self.assertWarnsRegex( + OptionalDependencyImportWarning, + rf"module '{dummy}' failed to import with: ModuleNotFoundError.*__qiskit__", + ): + self.assertFalse(tester) + def test_import_allows_attributes_failure(self): """Check that the import tester can accept a dictionary mapping module names to attributes, and that these are recognised when they are missing.""" @@ -334,7 +432,8 @@ def test_import_allows_attributes_failure(self): } feature = LazyImportTester(name_map) - self.assertFalse(feature) + with self.assertWarnsRegex(UserWarning, r"'builtins' imported, but attribute"): + self.assertFalse(feature) def test_import_fails_with_no_modules(self): """Catch programmer errors with no modules to test.""" diff --git a/test/python/visualization/references/pass_manager_standard.dot b/test/python/visualization/references/pass_manager_standard.dot index e1c5d3979ca2..bdb2137f33a3 100644 --- a/test/python/visualization/references/pass_manager_standard.dot +++ b/test/python/visualization/references/pass_manager_standard.dot @@ -10,7 +10,7 @@ labeljust=l; subgraph cluster_3 { fontname=helvetica; -label="[1] condition"; +label="[1] ConditionalController"; labeljust=l; 4 [color=red, fontname=helvetica, label=TrivialLayout, shape=rectangle]; 5 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; @@ -58,7 +58,7 @@ labeljust=l; subgraph cluster_17 { fontname=helvetica; -label="[6] do_while"; +label="[6] DoWhileController"; labeljust=l; 18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; 19 [color=black, fontname=helvetica, fontsize=10, label=label, shape=ellipse, style=dashed]; diff --git a/test/python/visualization/references/pass_manager_style.dot b/test/python/visualization/references/pass_manager_style.dot index 0c80853d00be..3de498c4ada7 100644 --- a/test/python/visualization/references/pass_manager_style.dot +++ b/test/python/visualization/references/pass_manager_style.dot @@ -10,7 +10,7 @@ labeljust=l; subgraph cluster_3 { fontname=helvetica; -label="[1] condition"; +label="[1] ConditionalController"; labeljust=l; 4 [color=red, fontname=helvetica, label=TrivialLayout, shape=rectangle]; 5 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; @@ -58,7 +58,7 @@ labeljust=l; subgraph cluster_17 { fontname=helvetica; -label="[6] do_while"; +label="[6] DoWhileController"; labeljust=l; 18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; 19 [color=black, fontname=helvetica, fontsize=10, label=label, shape=ellipse, style=dashed]; diff --git a/test/python/visualization/test_pass_manager_drawer.py b/test/python/visualization/test_pass_manager_drawer.py index b3456b141406..6e53a895f67d 100644 --- a/test/python/visualization/test_pass_manager_drawer.py +++ b/test/python/visualization/test_pass_manager_drawer.py @@ -18,6 +18,7 @@ from qiskit.transpiler import CouplingMap, Layout from qiskit.transpiler.passmanager import PassManager from qiskit import QuantumRegister +from qiskit.passmanager.flow_controllers import ConditionalController, DoWhileController from qiskit.transpiler.passes import GateDirection, Unroller from qiskit.transpiler.passes import CheckMap from qiskit.transpiler.passes import SetLayout @@ -48,13 +49,17 @@ def setUp(self): # Create a pass manager with a variety of passes and flow control structures self.pass_manager = PassManager() self.pass_manager.append(SetLayout(layout)) - self.pass_manager.append(TrivialLayout(coupling_map), condition=lambda x: True) + self.pass_manager.append( + ConditionalController(TrivialLayout(coupling_map), condition=lambda x: True) + ) self.pass_manager.append(FullAncillaAllocation(coupling_map)) self.pass_manager.append(EnlargeWithAncilla()) with self.assertWarns(DeprecationWarning): self.pass_manager.append(Unroller(basis_gates)) self.pass_manager.append(CheckMap(coupling_map)) - self.pass_manager.append(BarrierBeforeFinalMeasurements(), do_while=lambda x: False) + self.pass_manager.append( + DoWhileController(BarrierBeforeFinalMeasurements(), do_while=lambda x: False) + ) self.pass_manager.append(GateDirection(coupling_map)) self.pass_manager.append(RemoveResetInZeroState()) diff --git a/tools/update_fake_backends.py b/tools/update_fake_backends.py index 32f26a1c2411..415a5c68fd55 100755 --- a/tools/update_fake_backends.py +++ b/tools/update_fake_backends.py @@ -19,7 +19,7 @@ import json import os -from qiskit.providers.ibmq import IBMQ +from qiskit_ibm_provider import IBMProvider from qiskit.circuit.parameterexpression import ParameterExpression @@ -57,9 +57,7 @@ def _main(): parser.add_argument("--hub", type=str, default=None) parser.add_argument("--group", type=str, default=None) args = parser.parse_args() - provider = IBMQ.load_account() - if args.hub or args.group or args.project: - provider = IBMQ.get_provider(hub=args.hub, group=args.group, project=args.project) + provider = IBMProvider() ibmq_backends = provider.backends() for backend in ibmq_backends: raw_name = backend.name()