From 547c40ad5a3fdef87dec3960f1267c11381b049d Mon Sep 17 00:00:00 2001 From: Scott Carda Date: Thu, 12 Mar 2026 13:37:18 -0700 Subject: [PATCH] Avoid wrapping non-QIR targets for Cirq and Qiskit generic wrappers --- azure-quantum/azure/quantum/cirq/service.py | 7 ++++ .../azure/quantum/qiskit/provider.py | 6 ++++ azure-quantum/tests/mock_client.py | 15 +++++++++ azure-quantum/tests/test_cirq.py | 32 +++++++++++++++++++ azure-quantum/tests/test_qiskit.py | 17 ++++++++++ 5 files changed, 77 insertions(+) diff --git a/azure-quantum/azure/quantum/cirq/service.py b/azure-quantum/azure/quantum/cirq/service.py index ca27a7ac..926b8a2b 100644 --- a/azure-quantum/azure/quantum/cirq/service.py +++ b/azure-quantum/azure/quantum/cirq/service.py @@ -96,6 +96,13 @@ def targets( cirq_targets.append(target) continue + # Only apply the generic QIR fallback to targets that advertise a QIR + # target profile. Providers like Pasqal use pulse-level input formats + # (e.g. pasqal.pulser.v1) that are incompatible with QIR submission. + # target_profile is None for non-QIR targets. + if not status.target_profile: + continue + cirq_targets.append( AzureGenericQirCirqTarget.from_target_status( self._workspace, pid, status, **kwargs diff --git a/azure-quantum/azure/quantum/qiskit/provider.py b/azure-quantum/azure/quantum/qiskit/provider.py index 31cf8798..838ad9c4 100644 --- a/azure-quantum/azure/quantum/qiskit/provider.py +++ b/azure-quantum/azure/quantum/qiskit/provider.py @@ -143,6 +143,12 @@ def backends(self, name=None, **kwargs): if (target_id, pid) in existing_pairs: continue status = status_by_target.get((target_id, pid)) + # Only create a generic QIR backend for targets that advertise a QIR + # target profile. Providers like Pasqal use pulse-level input formats + # (e.g. pasqal.pulser.v1) that are incompatible with QIR submission. + # target_profile is None for non-QIR targets. + if status is not None and not status.target_profile: + continue backend_list.append( AzureGenericQirBackend( name=target_id, diff --git a/azure-quantum/tests/mock_client.py b/azure-quantum/tests/mock_client.py index 2341b0d4..cf94bb36 100644 --- a/azure-quantum/tests/mock_client.py +++ b/azure-quantum/tests/mock_client.py @@ -766,6 +766,21 @@ def _target( ], } ), + # A non-QIR provider (no target_profile) that should never get a generic + # QIR backend/target wrapper — mirrors real-world Pasqal behavior. + ProviderStatus( + { + "id": "pasqal", + "currentAvailability": "Available", + "targets": [ + _target( + target_id="pasqal.sim.emu-tn", + num_qubits=100, + target_profile=None, + ) + ], + } + ), ] ws._client.services.providers._store[:] = providers diff --git a/azure-quantum/tests/test_cirq.py b/azure-quantum/tests/test_cirq.py index ef961aff..4c9b002e 100644 --- a/azure-quantum/tests/test_cirq.py +++ b/azure-quantum/tests/test_cirq.py @@ -233,6 +233,38 @@ def test_cirq_generic_to_cirq_result_drops_non_binary_shots_and_exposes_raw(): ) +def test_cirq_service_targets_excludes_non_qir_target(): + """Targets without a target_profile (e.g. Pasqal) must not be wrapped as + AzureGenericQirCirqTarget — they use pulse-level input formats incompatible + with QIR submission. + """ + pytest.importorskip("cirq") + pytest.importorskip("cirq_ionq") + + from azure.quantum.cirq.service import AzureQuantumService + from azure.quantum.cirq.targets.generic import AzureGenericQirCirqTarget + + from mock_client import create_default_workspace + + ws = create_default_workspace() + _freeze_workspace_client_recreation(ws) + service = AzureQuantumService(workspace=ws) + + targets = service.targets() + target_names = [t.name for t in targets] + + # The Pasqal target (target_profile=None) should be completely absent. + assert not any( + "pasqal" in name for name in target_names + ), f"Non-QIR Pasqal target unexpectedly appeared in service.targets(): {target_names}" + # Specifically, no AzureGenericQirCirqTarget should have been created for it. + assert not any( + isinstance(t, AzureGenericQirCirqTarget) and "pasqal" in t.name for t in targets + ) + # QIR-capable targets should still be present. + assert any("microsoft.estimator" in name for name in target_names) + + def test_cirq_service_targets_discovers_provider_specific_and_generic_wrappers( monkeypatch: pytest.MonkeyPatch, ): diff --git a/azure-quantum/tests/test_qiskit.py b/azure-quantum/tests/test_qiskit.py index 141c1b4d..408130de 100644 --- a/azure-quantum/tests/test_qiskit.py +++ b/azure-quantum/tests/test_qiskit.py @@ -519,6 +519,23 @@ def test_qir_target_profile_from_deprecated_target_capability(): assert "target_profile" not in input_params +def test_generic_qir_backend_not_created_for_non_qir_target(): + """Targets without a target_profile (e.g. Pasqal) must not receive an + AzureGenericQirBackend — they use pulse-level input formats incompatible + with QIR submission. + + The default mock workspace already includes pasqal.sim.emu-tn with + target_profile=None via seed_providers in mock_client.py. + """ + from qiskit.providers.exceptions import QiskitBackendNotFoundError + + ws = create_default_workspace() + provider = AzureQuantumProvider(workspace=ws) + + with pytest.raises(QiskitBackendNotFoundError): + provider.get_backend("pasqal.sim.emu-tn") + + def test_generic_qir_backend_created_for_unknown_workspace_target( monkeypatch: pytest.MonkeyPatch, ):