Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tests for Dynamical Decoupling #153

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
Changelog
=========

Version 20.9
============

* 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.8
============

Expand Down
15 changes: 7 additions & 8 deletions src/iqm/iqm_client/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,29 +652,28 @@ 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`,
but users can use this class to provide their own dynamical decoupling strategies.

See :cite:`Ezzell_2022` for information on DD sequences.
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 @@ -230,6 +232,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 @@ -666,5 +678,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 @@ -32,6 +32,8 @@
CircuitExecutionError,
CircuitValidationError,
ClientConfigurationError,
DDMode,
DDStrategy,
DynamicQuantumArchitecture,
HeraldingMode,
Instruction,
Expand Down Expand Up @@ -238,6 +240,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 @@ -272,6 +293,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
Loading