diff --git a/qiskit_ibm_runtime/base_primitive.py b/qiskit_ibm_runtime/base_primitive.py index c7bb67fb9..33c928d30 100644 --- a/qiskit_ibm_runtime/base_primitive.py +++ b/qiskit_ibm_runtime/base_primitive.py @@ -29,6 +29,7 @@ from .runtime_job import RuntimeJob from .ibm_backend import IBMBackend from .utils.default_session import get_cm_session +from .utils.utils import validate_isa_circuits from .constants import DEFAULT_DECODERS from .qiskit_runtime_service import QiskitRuntimeService @@ -137,6 +138,14 @@ def _run_primitive(self, primitive_inputs: Dict, user_kwargs: Dict) -> RuntimeJo Returns: Submitted job. """ + if ( + self._backend + and isinstance(self._backend, IBMBackend) + and isinstance(self._backend._service, QiskitRuntimeService) + and hasattr(self._backend, "target") + ): + validate_isa_circuits(primitive_inputs["circuits"], self._backend.target) + combined = Options._merge_options(self._options, user_kwargs) if self._backend: diff --git a/qiskit_ibm_runtime/utils/backend_decoder.py b/qiskit_ibm_runtime/utils/backend_decoder.py index d02fb90fb..369be0791 100644 --- a/qiskit_ibm_runtime/utils/backend_decoder.py +++ b/qiskit_ibm_runtime/utils/backend_decoder.py @@ -95,15 +95,16 @@ def properties_from_server_data(properties: Dict) -> BackendProperties: Returns: A ``BackendProperties`` instance. """ - properties["last_update_date"] = dateutil.parser.isoparse(properties["last_update_date"]) - for qubit in properties["qubits"]: - for nduv in qubit: - nduv["date"] = dateutil.parser.isoparse(nduv["date"]) - for gate in properties["gates"]: - for param in gate["parameters"]: - param["date"] = dateutil.parser.isoparse(param["date"]) - for gen in properties["general"]: - gen["date"] = dateutil.parser.isoparse(gen["date"]) + if isinstance(properties["last_update_date"], str): + properties["last_update_date"] = dateutil.parser.isoparse(properties["last_update_date"]) + for qubit in properties["qubits"]: + for nduv in qubit: + nduv["date"] = dateutil.parser.isoparse(nduv["date"]) + for gate in properties["gates"]: + for param in gate["parameters"]: + param["date"] = dateutil.parser.isoparse(param["date"]) + for gen in properties["general"]: + gen["date"] = dateutil.parser.isoparse(gen["date"]) properties = utc_to_local_all(properties) return BackendProperties.from_dict(properties) diff --git a/qiskit_ibm_runtime/utils/utils.py b/qiskit_ibm_runtime/utils/utils.py index 23831b2f6..0108a8474 100644 --- a/qiskit_ibm_runtime/utils/utils.py +++ b/qiskit_ibm_runtime/utils/utils.py @@ -17,9 +17,10 @@ import os import re import hashlib +import warnings from queue import Queue from threading import Condition -from typing import List, Optional, Any, Dict, Union, Tuple +from typing import List, Optional, Any, Dict, Union, Tuple, Sequence from urllib.parse import urlparse import requests @@ -27,9 +28,50 @@ IAMAuthenticator, ) from ibm_platform_services import ResourceControllerV2 # pylint: disable=import-error +from qiskit.circuit import QuantumCircuit +from qiskit.transpiler import Target from qiskit_ibm_runtime.exceptions import IBMInputValueError +def is_isa_circuit(circuit: QuantumCircuit, target: Target) -> bool: + """Checks if the circuit is an ISA circuit, meaning that it has a layout and that it + only uses instructions that exist in the target. + Args: + circuit: A single QuantumCircuit + target: A Qiskit Target + Returns: + Boolean True if the circuit is an ISA circuit + """ + if circuit.layout is None: + return False + for instruction in circuit.data: + name = instruction.operation.name + qargs = tuple(circuit.find_bit(x).index for x in instruction.qubits) + if not target.instruction_supported(name, qargs) and name != "barrier": + return False + return True + + +def validate_isa_circuits(circuits: Sequence[QuantumCircuit], target: Target) -> None: + """Validate if all circuits are ISA circuits + Args: + circuits: A list of QuantumCircuits + target: A Qiskit Target + Raises: + NonISACircuitsError if some of the circuits are not ISA circuits + """ + if not all(is_isa_circuit(circuit, target) for circuit in circuits): + warnings.warn( + "Circuits that do not match the target hardware definition will no longer be supported " + "after March 1, 2024. See the transpilation documentation " + "(https://docs.quantum.ibm.com/transpile) for instructions to transform circuits and " + "the primitive examples (https://docs.quantum.ibm.com/run/primitives-examples) to see " + "this coupled with operator transformations.", + DeprecationWarning, + stacklevel=6, + ) + + def validate_job_tags(job_tags: Optional[List[str]]) -> None: """Validates input job tags. diff --git a/releasenotes/notes/deprecate-abstract-circuits-d9bdc94b2be7ea21.yaml b/releasenotes/notes/deprecate-abstract-circuits-d9bdc94b2be7ea21.yaml new file mode 100644 index 000000000..666ce3e4b --- /dev/null +++ b/releasenotes/notes/deprecate-abstract-circuits-d9bdc94b2be7ea21.yaml @@ -0,0 +1,7 @@ +--- +deprecations: + - | + Circuits that do not match the target hardware definition will no longer be supported after March 1, 2024. + See the transpilation documentation (https://docs.quantum.ibm.com/transpile) for instructions to + transform circuits and the primitive examples (https://docs.quantum.ibm.com/run/primitives-examples) + to see this coupled with operator transformations. \ No newline at end of file