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 heralding option #87

Merged
merged 5 commits into from
Jun 2, 2023
Merged
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
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 12.5
============

* Add parameter ``heralding`` to ``RunRequest``. `#87 <https://github.com/iqm-finland/iqm-client/pull/87>`_

Version 12.4
============

Expand Down
20 changes: 20 additions & 0 deletions src/iqm_client/iqm_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,21 @@ class SingleQubitMapping(BaseModel):
for all qubits in the circuit."""


class HeraldingMode(str, Enum):
"""Heralding mode for circuit execution.

Heralding is the practice of generating data about the state of qubits prior to execution of a circuit.
This can be achieved by measuring the qubits immediately before executing each shot for a circuit."""

NONE = 'none'
"""Do not do any heralding."""
ZEROS = 'zeros'
"""Perform a heralding measurement, only retain shots with an all-zeros result.

Note: in this mode, the number of shots returned after execution will be less or equal to the requested amount
due to the post-selection based on heralding data."""


class RunRequest(BaseModel):
"""Request for an IQM quantum computer to run a job that executes a batch of quantum circuits.

Expand All @@ -410,6 +425,8 @@ class RunRequest(BaseModel):
circuit_duration_check: bool = Field(True)
"""If True (default), circuits are disqualified on the server if they are too long compared to the
T2 decoherence times of the QPU. Setting it to False disables the check, which should not be done in normal use."""
heralding_mode: HeraldingMode = Field(HeraldingMode.NONE)
"""which heralding mode to use during the execution of circuits in this request."""


CircuitMeasurementResults = dict[str, list[list[int]]]
Expand Down Expand Up @@ -752,6 +769,7 @@ def submit_circuits(
calibration_set_id: Optional[UUID] = None,
shots: int = 1,
circuit_duration_check: bool = True,
heralding_mode: HeraldingMode = HeraldingMode.NONE,
) -> UUID:
"""Submits a batch of quantum circuits for execution on a quantum computer.

Expand All @@ -765,6 +783,7 @@ def submit_circuits(
calibration_set_id: ID of the calibration set to use, or ``None`` to use the latest one
shots: number of times ``circuits`` are executed
circuit_duration_check: whether to enable max circuit duration criteria for disqualification
heralding_mode: Heralding mode to use during the execution.

Returns:
ID for the created job. This ID is needed to query the job status and the execution results.
Expand Down Expand Up @@ -806,6 +825,7 @@ def submit_circuits(
calibration_set_id=calibration_set_id,
shots=shots,
circuit_duration_check=circuit_duration_check,
heralding_mode=heralding_mode,
)

headers = {'Expect': '100-Continue', 'User-Agent': self._signature}
Expand Down
41 changes: 40 additions & 1 deletion tests/test_iqm_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
# limitations under the License.
"""Tests for the IQM client.
"""
from mockito import ANY, verify, when
import json

# pylint: disable=unused-argument
from mockito import ANY, kwargs, unstub, verify, when
import pytest
import requests
from requests import HTTPError
Expand All @@ -28,6 +29,7 @@
ClientConfigurationError,
IQMClient,
QuantumArchitectureSpecification,
RunRequest,
SingleQubitMapping,
Status,
__version__,
Expand Down Expand Up @@ -189,6 +191,43 @@ def test_submit_circuits_with_duration_check_disabled_returns_id(
assert job_id == existing_run


def test_submit_circuits_does_not_activate_heralding_by_default(base_url, sample_circuit):
client = IQMClient(base_url)
circuits = [Circuit.parse_obj(sample_circuit)]
expected_request_json = json.loads(
RunRequest(circuits=circuits, shots=10, heralding_mode='none').json(exclude_none=True)
)
when(requests).post(f'{base_url}/jobs', json=expected_request_json, **kwargs).thenReturn(
MockJsonResponse(201, {'id': str(existing_run)})
)
client.submit_circuits(circuits=circuits, shots=10)
unstub()


def test_submit_circuits_sets_heralding_mode_in_run_request(base_url, sample_circuit):
client = IQMClient(base_url)
circuits = [Circuit.parse_obj(sample_circuit)]
expected_request_json = json.loads(
RunRequest(circuits=circuits, shots=1, heralding_mode='zeros').json(exclude_none=True)
)
when(requests).post(f'{base_url}/jobs', json=expected_request_json, **kwargs).thenReturn(
MockJsonResponse(201, {'id': str(existing_run)})
)
client.submit_circuits(circuits=circuits, heralding_mode='zeros')
unstub()


def test_submit_circuits_raises_with_invalid_heralding_mode(base_url, sample_circuit):
client = IQMClient(base_url)
with pytest.raises(ValueError, match='value is not a valid enumeration member'):
client.submit_circuits(
qubit_mapping={'Qubit A': 'QB1', 'Qubit B': 'QB2'},
circuits=[Circuit.parse_obj(sample_circuit)],
heralding_mode='bamboleo',
shots=1000,
)


def test_get_run_adds_user_agent(mock_server, base_url, calibration_set_id, sample_circuit):
"""
Tests that get_run without client signature adds the correct User-Agent header
Expand Down