diff --git a/qiskit_ibm_runtime/utils/utils.py b/qiskit_ibm_runtime/utils/utils.py index 168f6c7f2..e7eed6d53 100644 --- a/qiskit_ibm_runtime/utils/utils.py +++ b/qiskit_ibm_runtime/utils/utils.py @@ -47,6 +47,34 @@ def is_simulator(backend: BackendV1 | BackendV2) -> bool: return getattr(backend, "simulator", False) +def _is_isa_circuit_helper(circuit: QuantumCircuit, target: Target) -> str: + """ + A section of is_isa_circuit, separated to allow recursive calls + within blocks of conditional operatiosn. + """ + for instruction in circuit.data: + operation = instruction.operation + + name = operation.name + qargs = tuple(circuit.find_bit(x).index for x in instruction.qubits) + if ( + not target.instruction_supported(name, qargs) + and name != "barrier" + and not circuit.has_calibration_for(instruction) + ): + return ( + f"The instruction {name} on qubits {qargs} is not supported by the target system." + ) + + if isinstance(operation, ControlFlowOp): + for sub_circ in operation.blocks: + sub_string = _is_isa_circuit_helper(sub_circ, target) + if sub_string: + return sub_string + + return "" + + def is_isa_circuit(circuit: QuantumCircuit, target: Target) -> str: """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. @@ -64,18 +92,7 @@ def is_isa_circuit(circuit: QuantumCircuit, target: Target) -> str: f"but the target system requires {target.num_qubits} qubits." ) - 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" - and not circuit.has_calibration_for(instruction) - ): - return ( - f"The instruction {name} on qubits {qargs} is not supported by the target system." - ) - return "" + return _is_isa_circuit_helper(circuit, target) def are_circuits_dynamic(circuits: List[QuantumCircuit], qasm_default: bool = True) -> bool: diff --git a/test/unit/test_estimator.py b/test/unit/test_estimator.py index f36e4b732..b0d3d8543 100644 --- a/test/unit/test_estimator.py +++ b/test/unit/test_estimator.py @@ -21,6 +21,7 @@ from qiskit.primitives.containers.estimator_pub import EstimatorPub from qiskit_ibm_runtime import Estimator, Session, EstimatorV2, EstimatorOptions, IBMInputValueError +from qiskit_ibm_runtime.fake_provider import FakeSherbrooke from .mock.fake_runtime_service import FakeRuntimeService from ..ibm_test_case import IBMTestCase @@ -286,3 +287,22 @@ def test_estimator_validations(self): obs = [] with self.assertRaisesRegex(ValueError, "Empty observables array is not allowed"): inst.run(pubs=[(circ, obs)]) + + def test_gate_not_in_target(self): + """Test exception when circuits contain gates that are not basis gates""" + backend = FakeSherbrooke() + estimator = EstimatorV2(backend=backend) + observable = SparsePauliOp("Z") + + circ = QuantumCircuit(1, 1) + circ.x(0) + circ.measure(0, 0) + with circ.if_test((0, 1)): + with circ.if_test((0, 0)) as else_: + circ.x(0) + with else_: + circ.h(0) + circ.measure(0, 0) + + with self.assertRaisesRegex(IBMInputValueError, " h "): + estimator.run(pubs=[(circ, observable)])