Skip to content

Commit

Permalink
remote: Allow passing of options in get_backend()
Browse files Browse the repository at this point in the history
When we introduced the local provider, we made it so we declare
its options (nb_photons, kappa_1...) when using get_backend().

This is contradictory to what is happening in the remote provider,
where options must be passed in execute().

This commit harmonizes this behaviour by allowing to set the options
in get_backend() for the remote provider.
(which actually makes some sense to do it like this when we want to
set the nb_photons for example).
This allows to quickly swap between the local and remote provider.

We aim to stay as permissive as possible, by still letting the user
passing options at execute() for the remote provider.
  • Loading branch information
Maxence Drutel committed Nov 29, 2023
1 parent 25eb1c3 commit e9da331
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 8 deletions.
14 changes: 9 additions & 5 deletions qiskit_alice_bob_provider/remote/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions qiskit_alice_bob_provider/remote/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Expand Down
6 changes: 4 additions & 2 deletions tests/remote/test_against_real_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
36 changes: 35 additions & 1 deletion tests/remote/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down

0 comments on commit e9da331

Please sign in to comment.