Skip to content

Commit

Permalink
Add Tests for Dynamical Decoupling (#153)
Browse files Browse the repository at this point in the history
* Define DDStrategy as Pydantic BaseModel instead of dataclass

Having DDStrategy as BaseModel helps to do deserialization from JSON properly without implementing any custom deserializers

* Add unit tests to test run requests with DD

* Add DD to Metadata model tests

* Fix formatting

* Update CHANGELOG

* Fix pylint errors

* Update docstring

Remove ``cite`` role as no bibliography file is provided

Co-authored-by: Ville Bergholm <[email protected]>

* Update CHANGELOG.rst

---------

Co-authored-by: Ville Bergholm <[email protected]>
Co-authored-by: Rakhim Davletkaliyev <[email protected]>
  • Loading branch information
3 people authored Feb 7, 2025
1 parent 540f662 commit 3fc2a29
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 13 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
Changelog
=========

Version 20.16
=============

* Define ``DDStrategy`` as Pydantic ``BaseModel`` `#153 <https://github.com/iqm-finland/iqm-client/pull/153>`_
* Add unit tests to test ``RunRequest`` with dynamical decoupling `#153 <https://github.com/iqm-finland/iqm-client/pull/153>`_

Version 20.15
=============
Expand Down
13 changes: 6 additions & 7 deletions src/iqm/iqm_client/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,8 +653,7 @@ class DDMode(str, Enum):
respectively."""


@dataclass
class DDStrategy:
class DDStrategy(BaseModel):
"""Describes a particular dynamical decoupling strategy.
The current standard DD stategy can be found in :attr:`.STANDARD_DD_STRATEGY`,
Expand All @@ -663,19 +662,19 @@ class DDStrategy:
See Ezzell et al., Phys. Rev. Appl. 20, 064027 (2022) for information on DD sequences.
"""

merge_contiguous_waits: bool = True
merge_contiguous_waits: bool = Field(True)
"""Merge contiguous ``Wait`` instructions into one if they are separated only by ``Block`` instructions."""

target_qubits: Optional[frozenset[str]] = None
target_qubits: Optional[frozenset[str]] = Field(None)
"""Qubits on which dynamical decoupling should be applied. If ``None``, all qubits are targeted."""

skip_leading_wait: bool = True
skip_leading_wait: bool = Field(True)
"""Skip processing leading ``Wait`` instructions."""

skip_trailing_wait: bool = True
skip_trailing_wait: bool = Field(True)
"""Skip processing trailing ``Wait`` instructions."""

gate_sequences: list[tuple[int, Union[str, PRXSequence], str]] = field(default_factory=list)
gate_sequences: list[tuple[int, Union[str, PRXSequence], str]] = Field(default_factory=list)
"""Available decoupling gate sequences to chose from in this strategy.
Each sequence is defined by a tuple of ``(ratio, gate pattern, align)``:
Expand Down
14 changes: 14 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
REQUESTS_TIMEOUT,
Circuit,
CircuitCompilationOptions,
DDMode,
DDStrategy,
DynamicQuantumArchitecture,
GateImplementationInfo,
GateInfo,
Expand Down Expand Up @@ -236,6 +238,16 @@ def run_request_with_heralding(sample_circuit) -> RunRequest:
)


@pytest.fixture()
def run_request_with_dd(sample_circuit) -> RunRequest:
return RunRequest(
circuits=[sample_circuit],
shots=10,
dd_mode=DDMode.ENABLED,
dd_strategy=DDStrategy(gate_sequences=[(9, 'XYXYYXYX', 'asap'), (5, 'YXYX', 'asap'), (2, 'XX', 'center')]),
)


@pytest.fixture()
def run_request_with_move_validation(sample_circuit) -> RunRequest:
return RunRequest(
Expand Down Expand Up @@ -703,5 +715,7 @@ def submit_circuits_args(run_request: RunRequest) -> dict[str, Any]:
heralding_mode=run_request.heralding_mode,
move_gate_validation=run_request.move_validation_mode,
move_gate_frame_tracking=run_request.move_gate_frame_tracking_mode,
dd_mode=run_request.dd_mode,
dd_strategy=run_request.dd_strategy,
),
}
49 changes: 49 additions & 0 deletions tests/test_iqm_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
CircuitExecutionError,
CircuitValidationError,
ClientConfigurationError,
DDMode,
DDStrategy,
Counts,
DynamicQuantumArchitecture,
HeraldingMode,
Expand Down Expand Up @@ -246,6 +248,25 @@ def test_submit_circuits_does_not_activate_heralding_by_default(
unstub()


def test_submit_circuits_does_not_activate_dd_by_default(
sample_client, jobs_url, minimal_run_request, submit_success, dynamic_architecture_url, dynamic_architecture_success
):
"""
Test submitting run request without dynamical decoupling
"""
# Expect request to have dynamical decoupling mode DISABLED by default
assert post_jobs_args(minimal_run_request)['json']['dd_mode'] == DDMode.DISABLED.value

expect(requests, times=1).post(jobs_url, **post_jobs_args(minimal_run_request)).thenReturn(submit_success)
when(requests).get(dynamic_architecture_url, ...).thenReturn(dynamic_architecture_success)

# Specify no dynamical decoupling mode in submit_circuits
sample_client.submit_circuits(circuits=minimal_run_request.circuits, shots=minimal_run_request.shots)

verifyNoUnwantedInteractions()
unstub()


def test_submit_circuits_raises_with_invalid_shots(sample_client, minimal_run_request):
"""
Test that submitting run request with invalid number of shots raises ValueError
Expand Down Expand Up @@ -280,6 +301,34 @@ def test_submit_circuits_sets_heralding_mode_in_run_request(
unstub()


def test_submit_circuits_sets_dd_mode_in_run_request(
sample_client,
jobs_url,
run_request_with_dd,
submit_success,
dynamic_architecture_url,
dynamic_architecture_success,
):
"""
Test submitting run request with dynamical decoupling
"""
# Expect dynamical decoupling mode to be the same as in run request
expected_dd_mode = run_request_with_dd.dd_mode.value
expected_dd_strategy = run_request_with_dd.dd_strategy

assert post_jobs_args(run_request_with_dd)['json']['dd_mode'] == expected_dd_mode
assert DDStrategy(**post_jobs_args(run_request_with_dd)['json']['dd_strategy']) == expected_dd_strategy
expect(requests, times=1).post(jobs_url, **post_jobs_args(run_request_with_dd)).thenReturn(submit_success)
expect(requests, times=1).get(dynamic_architecture_url, ...).thenReturn(dynamic_architecture_success)

assert submit_circuits_args(run_request_with_dd)['options'].dd_mode == expected_dd_mode
assert submit_circuits_args(run_request_with_dd)['options'].dd_strategy == expected_dd_strategy
sample_client.submit_circuits(**submit_circuits_args(run_request_with_dd))

verifyNoUnwantedInteractions()
unstub()


def test_submit_circuits_gets_architecture_once(
sample_client,
jobs_url,
Expand Down
26 changes: 20 additions & 6 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from iqm.iqm_client.models import (
CircuitBatch,
DDMode,
DynamicQuantumArchitecture,
GateImplementationInfo,
GateInfo,
Expand All @@ -43,25 +44,38 @@ def heralding_mode():
return HeraldingMode.ZEROS


@pytest.fixture
def dd_mode():
return DDMode.ENABLED


@pytest.mark.parametrize(
'metadata_factory',
[
# V1 and RESONANCE_V1
lambda shots, circuits_batch, heralding_mode: Metadata(
request=RunRequest(circuits=circuits_batch, shots=shots, heralding_mode=heralding_mode)
lambda shots, circuits_batch, heralding_mode, dd_mode: Metadata(
request=RunRequest(
circuits=circuits_batch,
shots=shots,
heralding_mode=heralding_mode,
dd_mode=dd_mode,
)
),
# V2
lambda shots, circuits_batch, heralding_mode: Metadata(
parameters=JobParameters(shots=shots, heralding_mode=heralding_mode), circuits_batch=circuits_batch
lambda shots, circuits_batch, heralding_mode, dd_mode: Metadata(
parameters=JobParameters(shots=shots, heralding_mode=heralding_mode, dd_mode=dd_mode),
circuits_batch=circuits_batch,
),
],
)
def test_metadata(metadata_factory, shots, circuits_batch, heralding_mode):
def test_metadata(metadata_factory, shots, circuits_batch, heralding_mode, dd_mode):
"""Tests different modes of Metadata class initialization."""
metadata = metadata_factory(shots, circuits_batch, heralding_mode)
metadata = metadata_factory(shots, circuits_batch, heralding_mode, dd_mode)
assert metadata.shots == shots
assert metadata.circuits == circuits_batch
assert metadata.heralding_mode == heralding_mode
assert metadata.dd_mode == dd_mode
assert metadata.dd_strategy is None


def test_gate_info_loci():
Expand Down

0 comments on commit 3fc2a29

Please sign in to comment.