Skip to content

Commit

Permalink
Added compiler option support to the IQM Sampler
Browse files Browse the repository at this point in the history
  • Loading branch information
Arianne Meijer committed Jul 18, 2024
1 parent 45857ec commit 907190d
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 33 deletions.
16 changes: 5 additions & 11 deletions src/iqm/cirq_iqm/iqm_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

from iqm.cirq_iqm.devices.iqm_device import IQMDevice, IQMDeviceMetadata
from iqm.cirq_iqm.serialize import serialize_circuit
from iqm.iqm_client import HeraldingMode, IQMClient, JobAbortionError, RunRequest
from iqm.iqm_client import IQMClient, JobAbortionError, RunRequest, CircuitCompilationOptions


class IQMSampler(cirq.work.Sampler):
Expand All @@ -43,10 +43,7 @@ class IQMSampler(cirq.work.Sampler):
ID of the calibration set to use. If ``None``, use the latest one.
run_sweep_timeout:
timeout to poll sweep results in seconds.
max_circuit_duration_over_t2: Circuits are disqualified on the server if they are longer than
this ratio of the T2 time of the qubits. If set to 0.0, no circuits are disqualified.
If set to None the server default value is used.
heralding_mode: Heralding mode to use during execution.
compiler_options: The compilation options to use for the circuits as defined by IQM Client.
Keyword Args:
auth_server_url (str): URL of user authentication server, if required by the IQM Cortex server.
Expand All @@ -64,8 +61,7 @@ def __init__(
*,
calibration_set_id: Optional[UUID] = None,
run_sweep_timeout: Optional[int] = None,
max_circuit_duration_over_t2: Optional[float] = None,
heralding_mode: HeraldingMode = HeraldingMode.NONE,
compiler_options: Optional[CircuitCompilationOptions] = None,
**user_auth_args, # contains keyword args auth_server_url, username and password
):
self._client = IQMClient(url, client_signature=f'cirq-iqm {version("cirq-iqm")}', **user_auth_args)
Expand All @@ -76,8 +72,7 @@ def __init__(
self._device = device
self._calibration_set_id = calibration_set_id
self._run_sweep_timeout = run_sweep_timeout
self._max_circuit_duration_over_t2 = max_circuit_duration_over_t2
self._heralding_mode = heralding_mode
self._compiler_options = compiler_options if compiler_options is not None else CircuitCompilationOptions()

@property
def device(self) -> IQMDevice:
Expand Down Expand Up @@ -163,8 +158,7 @@ def _send_circuits(
serialized_circuits,
calibration_set_id=self._calibration_set_id,
shots=repetitions,
max_circuit_duration_over_t2=self._max_circuit_duration_over_t2,
heralding_mode=self._heralding_mode,
options=self._compiler_options,
)
timeout_arg = [self._run_sweep_timeout] if self._run_sweep_timeout is not None else []

Expand Down
48 changes: 26 additions & 22 deletions tests/test_iqm_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
HeraldingMode,
Instruction,
IQMClient,
CircuitCompilationOptions,
JobAbortionError,
Metadata,
RunRequest,
Expand Down Expand Up @@ -81,8 +82,6 @@ def submit_circuits_default_kwargs() -> dict:
return {
'calibration_set_id': None,
'shots': 1,
'max_circuit_duration_over_t2': None,
'heralding_mode': HeraldingMode.NONE,
}


Expand All @@ -103,7 +102,7 @@ def test_run_sweep_executes_circuit_with_physical_names(
):
client = mock(IQMClient)
run_result = RunResult(status=Status.READY, measurements=[{'some stuff': [[0], [1]]}], metadata=iqm_metadata)
when(client).submit_circuits(ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)

adonis_sampler._client = client
Expand All @@ -122,7 +121,7 @@ def test_run_sweep_executes_circuit_with_calibration_set_id(
sampler = IQMSampler(base_url, Adonis(), calibration_set_id=calibration_set_id)
run_result = RunResult(status=Status.READY, measurements=[{'some stuff': [[0], [1]]}], metadata=iqm_metadata)
kwargs = submit_circuits_default_kwargs | {'calibration_set_id': calibration_set_id}
when(client).submit_circuits(ANY, **kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)

sampler._client = client
Expand All @@ -139,8 +138,8 @@ def test_run_sweep_has_duration_check_enabled_by_default(
client = mock(IQMClient)
sampler = IQMSampler(base_url, Adonis())
run_result = RunResult(status=Status.READY, measurements=[{'some stuff': [[0], [1]]}], metadata=iqm_metadata)
kwargs = submit_circuits_default_kwargs | {'max_circuit_duration_over_t2': None}
when(client).submit_circuits(ANY, **kwargs).thenReturn(job_id)
assert sampler._compiler_options.max_circuit_duration_over_t2 is None
when(client).submit_circuits(ANY, options=ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)

sampler._client = client
Expand All @@ -155,10 +154,12 @@ def test_run_sweep_executes_circuit_with_duration_check_disabled(
base_url, circuit_physical, iqm_metadata, submit_circuits_default_kwargs, job_id
):
client = mock(IQMClient)
sampler = IQMSampler(base_url, Adonis(), max_circuit_duration_over_t2=0.0)
sampler = IQMSampler(
base_url, Adonis(), compiler_options=CircuitCompilationOptions(max_circuit_duration_over_t2=0.0)
)
run_result = RunResult(status=Status.READY, measurements=[{'some stuff': [[0], [1]]}], metadata=iqm_metadata)
kwargs = submit_circuits_default_kwargs | {'max_circuit_duration_over_t2': 0.0}
when(client).submit_circuits(ANY, **kwargs).thenReturn(job_id)
assert sampler._compiler_options.max_circuit_duration_over_t2 == 0.0
when(client).submit_circuits(ANY, options=ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)

sampler._client = client
Expand All @@ -176,7 +177,7 @@ def test_run_sweep_allows_to_override_polling_timeout(
timeout = 123
sampler = IQMSampler(base_url, Adonis(), run_sweep_timeout=timeout)
run_result = RunResult(status=Status.READY, measurements=[{'some stuff': [[0], [1]]}], metadata=iqm_metadata)
when(client).submit_circuits(ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id, timeout).thenReturn(run_result)

sampler._client = client
Expand All @@ -193,8 +194,9 @@ def test_run_sweep_has_heralding_mode_none_by_default(
client = mock(IQMClient)
sampler = IQMSampler(base_url, Adonis())
run_result = RunResult(status=Status.READY, measurements=[{'some stuff': [[0], [1]]}], metadata=iqm_metadata)
kwargs = submit_circuits_default_kwargs | {'heralding_mode': HeraldingMode.NONE}
when(client).submit_circuits(ANY, **kwargs).thenReturn(job_id)
kwargs = submit_circuits_default_kwargs
assert sampler._compiler_options.heralding_mode is None
when(client).submit_circuits(ANY, options=ANY, **kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)

sampler._client = client
Expand All @@ -209,10 +211,12 @@ def test_run_sweep_executes_circuit_with_heralding_mode_zeros(
base_url, circuit_physical, iqm_metadata, submit_circuits_default_kwargs, job_id
):
client = mock(IQMClient)
sampler = IQMSampler(base_url, Adonis(), heralding_mode=HeraldingMode.ZEROS)
sampler = IQMSampler(
base_url, Adonis(), compiler_options=CircuitCompilationOptions(heralding_mode=HeraldingMode.ZEROS)
)
run_result = RunResult(status=Status.READY, measurements=[{'some stuff': [[0], [1]]}], metadata=iqm_metadata)
kwargs = submit_circuits_default_kwargs | {'heralding_mode': HeraldingMode.ZEROS}
when(client).submit_circuits(ANY, **kwargs).thenReturn(job_id)
assert sampler._compiler_options.heralding_mode == HeraldingMode.ZEROS
when(client).submit_circuits(ANY, options=ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)

sampler._client = client
Expand All @@ -228,7 +232,7 @@ def test_run_sweep_with_parameter_sweep(adonis_sampler, iqm_metadata, submit_cir
run_result = RunResult(
status=Status.READY, measurements=[{'some stuff': [[0]]}, {'some stuff': [[1]]}], metadata=iqm_metadata
)
when(client).submit_circuits(ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)
qubit_1 = cirq.NamedQubit('QB1')
qubit_2 = cirq.NamedQubit('QB2')
Expand All @@ -254,7 +258,7 @@ def test_run_sweep_abort_job_successful(
adonis_sampler, circuit_physical, submit_circuits_default_kwargs, job_id, recwarn
):
client = mock(IQMClient)
when(client).submit_circuits(ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenRaise(KeyboardInterrupt)
when(client).abort_job(job_id)
when(sys).exit().thenRaise(NotImplementedError) # just for testing without actually exiting python
Expand All @@ -271,7 +275,7 @@ def test_run_sweep_abort_job_successful(
@pytest.mark.usefixtures('unstub')
def test_run_sweep_abort_job_failed(adonis_sampler, circuit_physical, submit_circuits_default_kwargs, job_id):
client = mock(IQMClient)
when(client).submit_circuits(ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenRaise(KeyboardInterrupt)
when(client).abort_job(job_id).thenRaise(JobAbortionError)
when(sys).exit().thenRaise(NotImplementedError) # just for testing without actually exiting python
Expand Down Expand Up @@ -299,7 +303,7 @@ def test_run(adonis_sampler, iqm_metadata, submit_circuits_default_kwargs, job_i
status=Status.READY, measurements=[{'some stuff': [[0]]}, {'some stuff': [[1]]}], metadata=iqm_metadata
)
kwargs = submit_circuits_default_kwargs | {'shots': repetitions}
when(client).submit_circuits(ANY, **kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)

qubit_1 = cirq.NamedQubit('QB1')
Expand All @@ -323,7 +327,7 @@ def test_run_ndonis(device_with_resonator, base_url, iqm_metadata, submit_circui
status=Status.READY, measurements=[{'some stuff': [[0]]}, {'some stuff': [[1]]}], metadata=iqm_metadata
)
kwargs = submit_circuits_default_kwargs | {'shots': repetitions}
when(client).submit_circuits(ANY, **kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)

qubit_1, qubit_2 = device_with_resonator.qubits[:2]
Expand Down Expand Up @@ -353,7 +357,7 @@ def test_run_iqm_batch(adonis_sampler, iqm_metadata, submit_circuits_default_kwa
status=Status.READY, measurements=[{'some stuff': [[0]]}, {'some stuff': [[1]]}], metadata=iqm_metadata
)
kwargs = submit_circuits_default_kwargs | {'shots': repetitions}
when(client).submit_circuits(ANY, **kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id).thenReturn(run_result)

qubit_1 = cirq.NamedQubit('QB1')
Expand Down Expand Up @@ -382,7 +386,7 @@ def test_run_iqm_batch_allows_to_override_polling_timeout(
)
timeout = 123
sampler = IQMSampler(base_url, Adonis(), run_sweep_timeout=timeout)
when(client).submit_circuits(ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).submit_circuits(ANY, options=ANY, **submit_circuits_default_kwargs).thenReturn(job_id)
when(client).wait_for_results(job_id, timeout).thenReturn(run_result)

qubit_1 = cirq.NamedQubit('QB1')
Expand Down

0 comments on commit 907190d

Please sign in to comment.