diff --git a/qiskit_alice_bob_provider/remote/backend.py b/qiskit_alice_bob_provider/remote/backend.py index 829510a..7b5b0e3 100644 --- a/qiskit_alice_bob_provider/remote/backend.py +++ b/qiskit_alice_bob_provider/remote/backend.py @@ -67,6 +67,14 @@ def get_translation_stage_plugin(self): in translation_plugin.StatePreparationPlugin""" return self._translation_plugin + def update_options(self, option_updates: Dict[str, Any]) -> Options: + options: Options = self.options + for key, value in option_updates.items(): + if not hasattr(options, key): + raise ValueError(f'Backend does not support option "{key}"') + options.update_options(**{key: value}) + return options + def run(self, run_input: QuantumCircuit, **kwargs) -> AliceBobRemoteJob: """Run the quantum circuit on the Alice & Bob backend by calling the Alice & Bob API. @@ -83,11 +91,7 @@ def run(self, run_input: QuantumCircuit, **kwargs) -> AliceBobRemoteJob: Wait for the results by calling :func:`AliceBobRemoteJob.result`. """ - options: Options = self.options - for key, value in kwargs.items(): - if not hasattr(options, key): - raise ValueError(f'Backend does not support option "{key}"') - options.update_options(**{key: value}) + options = self.update_options(kwargs) input_params = _ab_input_params_from_options(options) job = jobs.create_job(self._api_client, self.name, input_params) run_input = PassManager([EnsurePreparationPass()]).run(run_input) diff --git a/qiskit_alice_bob_provider/remote/provider.py b/qiskit_alice_bob_provider/remote/provider.py index a92e3d1..bca015b 100644 --- a/qiskit_alice_bob_provider/remote/provider.py +++ b/qiskit_alice_bob_provider/remote/provider.py @@ -52,6 +52,14 @@ def __init__( for ab_target in list_targets(client): self._backends.append(AliceBobRemoteBackend(client, ab_target)) + def get_backend(self, name=None, **kwargs) -> AliceBobRemoteBackend: + backend = super().get_backend(name) + # We allow to set the options when getting the backend, + # to align with what we do in the local provider. + if kwargs: + backend.update_options(kwargs) + return backend + def backends( self, name: Optional[str] = None, **kwargs ) -> List[BackendV2]: diff --git a/tests/remote/test_against_real_server.py b/tests/remote/test_against_real_server.py index 94eda73..69447da 100644 --- a/tests/remote/test_against_real_server.py +++ b/tests/remote/test_against_real_server.py @@ -34,7 +34,9 @@ def test_happy_path(base_url: str, api_key: str) -> None: api_key=api_key, url=base_url, ) - backend = provider.get_backend('EMU:1Q:LESCANNE_2020') - job = execute(c, backend) + backend = provider.get_backend( + 'EMU:1Q:LESCANNE_2020', average_nb_photons=6.0 + ) + job = execute(c, backend, shots=100) res = job.result() res.get_counts() diff --git a/tests/remote/test_backend.py b/tests/remote/test_backend.py index 1e634d7..dd274f7 100644 --- a/tests/remote/test_backend.py +++ b/tests/remote/test_backend.py @@ -28,13 +28,47 @@ from qiskit_alice_bob_provider.remote.api.client import AliceBobApiException from qiskit_alice_bob_provider.remote.backend import ( + AliceBobRemoteBackend, _ab_input_params_from_options, _qiskit_to_qir, ) from qiskit_alice_bob_provider.remote.provider import AliceBobRemoteProvider -def test_options_validation(mocked_targets) -> None: +def test_get_backend(mocked_targets) -> None: + provider = AliceBobRemoteProvider(api_key='foo') + backend = provider.get_backend('EMU:1Q:LESCANNE_2020') + assert isinstance(backend, AliceBobRemoteBackend) + assert backend.options['average_nb_photons'] == 4.0 # Default value. + + +def test_get_backend_with_options(mocked_targets) -> None: + provider = AliceBobRemoteProvider(api_key='foo') + backend = provider.get_backend( + 'EMU:1Q:LESCANNE_2020', average_nb_photons=6.0 + ) + assert isinstance(backend, AliceBobRemoteBackend) + assert backend.options['average_nb_photons'] == 6.0 + + +def test_get_backend_options_validation(mocked_targets) -> None: + provider = AliceBobRemoteProvider(api_key='foo') + with pytest.raises(ValueError): + provider.get_backend('EMU:1Q:LESCANNE_2020', average_nb_photons=40) + with pytest.raises(ValueError): + provider.get_backend('EMU:1Q:LESCANNE_2020', average_nb_photons=-1) + with pytest.raises(ValueError): + provider.get_backend('EMU:1Q:LESCANNE_2020', shots=0) + with pytest.raises(ValueError): + provider.get_backend('EMU:1Q:LESCANNE_2020', shots=1e10) + with pytest.raises(ValueError): + provider.get_backend('EMU:1Q:LESCANNE_2020', bad_option=1) + + +def test_execute_options_validation(mocked_targets) -> None: + # We are permissive in our options sytem, allowing the user to both + # define options when creating the backend and executing. + # We therefore need to test both behaviors. c = QuantumCircuit(1, 1) provider = AliceBobRemoteProvider(api_key='foo') backend = provider.get_backend('EMU:1Q:LESCANNE_2020')