iqm-client 32.0.0__py3-none-any.whl → 33.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iqm/cirq_iqm/devices/iqm_device_metadata.py +2 -1
- iqm/cirq_iqm/examples/demo_common.py +1 -1
- iqm/cirq_iqm/examples/demo_iqm_execution.py +3 -3
- iqm/cirq_iqm/iqm_sampler.py +47 -29
- iqm/cirq_iqm/serialize.py +1 -1
- iqm/cirq_iqm/transpiler.py +3 -1
- iqm/iqm_client/__init__.py +0 -2
- iqm/iqm_client/errors.py +6 -17
- iqm/iqm_client/iqm_client.py +199 -602
- iqm/iqm_client/models.py +20 -611
- iqm/iqm_client/transpile.py +11 -8
- iqm/iqm_client/validation.py +18 -9
- iqm/iqm_server_client/__init__.py +14 -0
- iqm/iqm_server_client/errors.py +6 -0
- iqm/iqm_server_client/iqm_server_client.py +755 -0
- iqm/iqm_server_client/models.py +179 -0
- iqm/iqm_server_client/py.typed +0 -0
- iqm/qiskit_iqm/__init__.py +8 -0
- iqm/qiskit_iqm/examples/bell_measure.py +2 -2
- iqm/qiskit_iqm/examples/transpile_example.py +9 -4
- iqm/qiskit_iqm/fake_backends/fake_adonis.py +2 -1
- iqm/qiskit_iqm/fake_backends/fake_aphrodite.py +2 -1
- iqm/qiskit_iqm/fake_backends/fake_apollo.py +2 -1
- iqm/qiskit_iqm/fake_backends/fake_deneb.py +2 -1
- iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py +8 -7
- iqm/qiskit_iqm/iqm_backend.py +3 -4
- iqm/qiskit_iqm/iqm_circuit_validation.py +8 -7
- iqm/qiskit_iqm/iqm_job.py +106 -88
- iqm/qiskit_iqm/iqm_move_layout.py +2 -1
- iqm/qiskit_iqm/iqm_naive_move_pass.py +115 -56
- iqm/qiskit_iqm/iqm_provider.py +49 -36
- iqm/qiskit_iqm/iqm_target.py +12 -8
- iqm/qiskit_iqm/iqm_transpilation.py +219 -26
- iqm/qiskit_iqm/qiskit_to_iqm.py +150 -41
- iqm/qiskit_iqm/transpiler_plugins.py +11 -8
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/METADATA +4 -14
- iqm_client-33.0.0.dist-info/RECORD +63 -0
- iqm/iqm_client/api.py +0 -90
- iqm/iqm_client/authentication.py +0 -206
- iqm_client-32.0.0.dist-info/RECORD +0 -60
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/AUTHORS.rst +0 -0
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/LICENSE.txt +0 -0
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/WHEEL +0 -0
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/entry_points.txt +0 -0
- {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/top_level.txt +0 -0
iqm/iqm_client/models.py
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
r"""
|
|
14
|
+
r"""Data models used by IQMClient.
|
|
15
15
|
|
|
16
16
|
We currently support the following native operations for circuit execution,
|
|
17
17
|
represented by :class:`iqm.pulse.CircuitOperation`:
|
|
@@ -206,22 +206,24 @@ hardware can handle.
|
|
|
206
206
|
from __future__ import annotations
|
|
207
207
|
|
|
208
208
|
from dataclasses import dataclass
|
|
209
|
-
from enum import Enum
|
|
210
|
-
from functools import cached_property
|
|
211
|
-
import re
|
|
212
|
-
from typing import Any, TypeAlias
|
|
213
209
|
from uuid import UUID
|
|
214
210
|
|
|
215
|
-
from pydantic import BaseModel, Field
|
|
211
|
+
from pydantic import BaseModel, Field
|
|
216
212
|
|
|
217
213
|
from iqm.pulse import Circuit
|
|
218
214
|
from iqm.pulse.builder import build_quantum_ops
|
|
215
|
+
from iqm.station_control.interface.models import (
|
|
216
|
+
DDMode,
|
|
217
|
+
DDStrategy,
|
|
218
|
+
HeraldingMode,
|
|
219
|
+
MoveGateFrameTrackingMode,
|
|
220
|
+
MoveGateValidationMode,
|
|
221
|
+
QIRCode,
|
|
222
|
+
QubitMapping,
|
|
223
|
+
)
|
|
219
224
|
|
|
220
225
|
_SUPPORTED_OPERATIONS = build_quantum_ops({})
|
|
221
226
|
|
|
222
|
-
Locus: TypeAlias = tuple[StrictStr, ...]
|
|
223
|
-
"""Names of the QPU components (typically qubits) a quantum operation instance is acting on, e.g. `("QB1", "QB2")`."""
|
|
224
|
-
|
|
225
227
|
|
|
226
228
|
def _op_is_symmetric(name: str) -> bool:
|
|
227
229
|
"""Returns True iff the given native operation is symmetric, i.e. the order of the
|
|
@@ -254,13 +256,6 @@ def _op_arity(name: str) -> int:
|
|
|
254
256
|
return _SUPPORTED_OPERATIONS[name].arity
|
|
255
257
|
|
|
256
258
|
|
|
257
|
-
QIRCode: TypeAlias = str
|
|
258
|
-
"""QIR program code in string representation"""
|
|
259
|
-
|
|
260
|
-
CircuitBatch: TypeAlias = list[Circuit | QIRCode]
|
|
261
|
-
"""Type that represents a list of quantum circuits to be executed together in a single batch."""
|
|
262
|
-
|
|
263
|
-
|
|
264
259
|
def validate_circuit(circuit: Circuit) -> None:
|
|
265
260
|
"""Validates a submitted quantum circuit.
|
|
266
261
|
|
|
@@ -279,33 +274,6 @@ def validate_circuit(circuit: Circuit) -> None:
|
|
|
279
274
|
raise ValueError("Every circuit in a batch should be of type <Circuit> or <QIRCode>")
|
|
280
275
|
|
|
281
276
|
|
|
282
|
-
class SingleQubitMapping(BaseModel):
|
|
283
|
-
"""Mapping of a logical qubit name to a physical qubit name."""
|
|
284
|
-
|
|
285
|
-
logical_name: str = Field(..., examples=["alice"])
|
|
286
|
-
"""logical qubit name"""
|
|
287
|
-
physical_name: str = Field(..., examples=["QB1"])
|
|
288
|
-
"""physical qubit name"""
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
QubitMapping = list[SingleQubitMapping]
|
|
292
|
-
"""Type that represents a qubit mapping for a circuit, i.e. a list of single qubit mappings
|
|
293
|
-
for all qubits in the circuit."""
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
def serialize_qubit_mapping(qubit_mapping: dict[str, str]) -> list[SingleQubitMapping]:
|
|
297
|
-
"""Serializes a qubit mapping dict into the corresponding IQM data transfer format.
|
|
298
|
-
|
|
299
|
-
Args:
|
|
300
|
-
qubit_mapping: mapping from logical to physical qubit names
|
|
301
|
-
|
|
302
|
-
Returns:
|
|
303
|
-
data transfer object representing the mapping
|
|
304
|
-
|
|
305
|
-
"""
|
|
306
|
-
return [SingleQubitMapping(logical_name=k, physical_name=v) for k, v in qubit_mapping.items()]
|
|
307
|
-
|
|
308
|
-
|
|
309
277
|
class QuantumArchitectureSpecification(BaseModel):
|
|
310
278
|
"""Quantum architecture specification."""
|
|
311
279
|
|
|
@@ -373,283 +341,6 @@ class QuantumArchitecture(BaseModel):
|
|
|
373
341
|
"""Details about the quantum architecture."""
|
|
374
342
|
|
|
375
343
|
|
|
376
|
-
def _component_sort_key(component_name: str) -> tuple[str, int, str]:
|
|
377
|
-
"""Sorting key for QPU component names."""
|
|
378
|
-
|
|
379
|
-
def get_numeric_id(name: str) -> int:
|
|
380
|
-
match = re.search(r"(\d+)", name)
|
|
381
|
-
return int(match.group(1)) if match else 0
|
|
382
|
-
|
|
383
|
-
return re.sub(r"[^a-zA-Z]", "", component_name), get_numeric_id(component_name), component_name
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
class StaticQuantumArchitecture(BaseModel):
|
|
387
|
-
"""Static quantum architecture of the server.
|
|
388
|
-
|
|
389
|
-
The static quantum architecture (SQA) provides information about the QPU,
|
|
390
|
-
including the names of its computational components and the connections between them.
|
|
391
|
-
"""
|
|
392
|
-
|
|
393
|
-
# Optional *for now* (backwards compatible)
|
|
394
|
-
dut_label: str | None = None
|
|
395
|
-
"""Identifies the QPU."""
|
|
396
|
-
qubits: list[str] = Field(...)
|
|
397
|
-
"""Names of the physical qubits on the QPU, sorted."""
|
|
398
|
-
computational_resonators: list[str] = Field(...)
|
|
399
|
-
"""Names of the physical computational resonators on the QPU, sorted."""
|
|
400
|
-
connectivity: list[Locus] = Field(...)
|
|
401
|
-
"""Groups of components (qubits and computational resonators) that are connected by a coupler on the QPU, sorted."""
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
class QualityMetricSet(BaseModel):
|
|
405
|
-
"""Quality metrics for a calibration set."""
|
|
406
|
-
|
|
407
|
-
calibration_set_id: UUID | None = Field(...)
|
|
408
|
-
"""ID of the calibration set."""
|
|
409
|
-
calibration_set_dut_label: str = Field(...)
|
|
410
|
-
"""Chip Label of the calibration set."""
|
|
411
|
-
calibration_set_number_of_observations: int = Field(...)
|
|
412
|
-
"""Number of observations in the calibration set."""
|
|
413
|
-
calibration_set_created_timestamp: str = Field(...)
|
|
414
|
-
"""Timestamp when the calibration set was created."""
|
|
415
|
-
calibration_set_end_timestamp: str = Field(...)
|
|
416
|
-
"""Timestamp when the calibration set was finalized."""
|
|
417
|
-
calibration_set_is_invalid: bool = Field(...)
|
|
418
|
-
"""Whether the calibration set is invalid."""
|
|
419
|
-
quality_metric_set_id: UUID | None = Field(...)
|
|
420
|
-
"""ID of the quality metric set."""
|
|
421
|
-
quality_metric_set_dut_label: str | None = Field(...)
|
|
422
|
-
"""Chip label of the quality metric set."""
|
|
423
|
-
quality_metric_set_created_timestamp: str | None = Field(...)
|
|
424
|
-
"""Timestamp when the quality metric set was created."""
|
|
425
|
-
quality_metric_set_end_timestamp: str | None = Field(...)
|
|
426
|
-
"""Timestamp when the quality metric set was finalized."""
|
|
427
|
-
quality_metric_set_is_invalid: bool = Field(...)
|
|
428
|
-
"""Whether the quality metric set is invalid."""
|
|
429
|
-
metrics: dict[str, dict[str, Any]] | None = Field(...)
|
|
430
|
-
"""Quality metrics."""
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
class CalibrationSet(BaseModel):
|
|
434
|
-
"""Metadata and observations of a calibration set."""
|
|
435
|
-
|
|
436
|
-
calibration_set_id: UUID = Field(...)
|
|
437
|
-
"""ID of the calibration set."""
|
|
438
|
-
calibration_set_dut_label: str = Field(...)
|
|
439
|
-
"""Chip Label of the calibration set."""
|
|
440
|
-
calibration_set_is_invalid: bool = Field(...)
|
|
441
|
-
"""Whether the calibration set is invalid."""
|
|
442
|
-
calibration_set_created_timestamp: str = Field(...)
|
|
443
|
-
"""Timestamp when the calibration set was created."""
|
|
444
|
-
calibration_set_end_timestamp: str = Field(...)
|
|
445
|
-
"""Timestamp when the calibration set was finalized."""
|
|
446
|
-
observations: dict[str, Any] = Field(...)
|
|
447
|
-
"""Calibration data."""
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
class GateImplementationInfo(BaseModel):
|
|
451
|
-
"""Information about an implementation of a quantum gate/operation."""
|
|
452
|
-
|
|
453
|
-
loci: tuple[Locus, ...] = Field(...)
|
|
454
|
-
"""loci for which this gate implementation has been calibrated"""
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
class GateInfo(BaseModel):
|
|
458
|
-
"""Information about a quantum gate/operation."""
|
|
459
|
-
|
|
460
|
-
implementations: dict[str, GateImplementationInfo] = Field(...)
|
|
461
|
-
"""mapping of available implementation names to information about the implementations"""
|
|
462
|
-
default_implementation: str = Field(...)
|
|
463
|
-
"""default implementation for the gate, used unless overridden by :attr:`override_default_implementation`
|
|
464
|
-
or unless the user requests a specific implementation for a particular gate in the circuit using
|
|
465
|
-
:attr:`iqm.pulse.CircuitOperation.implementation`"""
|
|
466
|
-
override_default_implementation: dict[Locus, str] = Field(...)
|
|
467
|
-
"""mapping of loci to implementation names that override ``default_implementation`` for those loci"""
|
|
468
|
-
|
|
469
|
-
@field_validator("override_default_implementation", mode="before")
|
|
470
|
-
@classmethod
|
|
471
|
-
def override_default_implementation_validator(cls, value: Any) -> dict[Locus, str]:
|
|
472
|
-
"""Converts locus keys to tuples if they are encoded as strings."""
|
|
473
|
-
new_value = {}
|
|
474
|
-
if isinstance(value, dict):
|
|
475
|
-
for k, v in value.items():
|
|
476
|
-
if isinstance(k, tuple):
|
|
477
|
-
new_value[k] = v
|
|
478
|
-
# When Pydantic serializes a dict with tuple keys into JSON, the keys are turned into
|
|
479
|
-
# comma-separated strings (because JSON only supports string keys). Here we convert
|
|
480
|
-
# them back into tuples.
|
|
481
|
-
elif isinstance(k, str):
|
|
482
|
-
new_k = tuple(k.split(","))
|
|
483
|
-
new_value[new_k] = v
|
|
484
|
-
else:
|
|
485
|
-
raise ValueError("'override_default_implementation' keys must be strings or tuples.")
|
|
486
|
-
return new_value
|
|
487
|
-
raise ValueError("'override_default_implementation' must be a dict.")
|
|
488
|
-
|
|
489
|
-
@cached_property
|
|
490
|
-
def loci(self) -> tuple[Locus, ...]:
|
|
491
|
-
"""Returns all loci which are available for at least one of the implementations.
|
|
492
|
-
|
|
493
|
-
The loci are sorted first based on the first locus component, then the second, etc.
|
|
494
|
-
The sorting of individual locus components is first done alphabetically based on their
|
|
495
|
-
non-numeric part, and then components with the same non-numeric part are sorted numerically.
|
|
496
|
-
An example of loci sorted this way would be:
|
|
497
|
-
|
|
498
|
-
('QB1', 'QB2'),
|
|
499
|
-
('QB2', 'COMPR1'),
|
|
500
|
-
('QB2', 'QB3'),
|
|
501
|
-
('QB3', 'COMPR1'),
|
|
502
|
-
('QB3', 'COMPR2'),
|
|
503
|
-
('QB3', 'QB1'),
|
|
504
|
-
('QB10', 'QB2'),
|
|
505
|
-
|
|
506
|
-
"""
|
|
507
|
-
loci_set = {locus for impl in self.implementations.values() for locus in impl.loci}
|
|
508
|
-
loci_sorted = sorted(loci_set, key=lambda locus: tuple(map(_component_sort_key, locus)))
|
|
509
|
-
return tuple(loci_sorted)
|
|
510
|
-
|
|
511
|
-
def get_default_implementation(self, locus: Locus) -> str:
|
|
512
|
-
"""Default implementation of this gate for the given locus.
|
|
513
|
-
|
|
514
|
-
Args:
|
|
515
|
-
locus: gate locus
|
|
516
|
-
|
|
517
|
-
Returns:
|
|
518
|
-
Name of the default implementation of this gate for ``locus``.
|
|
519
|
-
|
|
520
|
-
"""
|
|
521
|
-
if (impl := self.override_default_implementation.get(locus)) is not None:
|
|
522
|
-
return impl
|
|
523
|
-
return self.default_implementation
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
class DynamicQuantumArchitecture(BaseModel):
|
|
527
|
-
"""Dynamic quantum architecture as returned by server.
|
|
528
|
-
|
|
529
|
-
The dynamic quantum architecture (DQA) describes gates/operations for which calibration data
|
|
530
|
-
exists in the calibration set.
|
|
531
|
-
"""
|
|
532
|
-
|
|
533
|
-
calibration_set_id: UUID = Field(...)
|
|
534
|
-
"""id of the calibration set from which this DQA was generated"""
|
|
535
|
-
qubits: list[str] = Field(...)
|
|
536
|
-
"""qubits that appear in at least one gate locus in the calibration set"""
|
|
537
|
-
computational_resonators: list[str] = Field(...)
|
|
538
|
-
"""computational resonators that appear in at least one gate locus in the calibration set"""
|
|
539
|
-
gates: dict[str, GateInfo] = Field(...)
|
|
540
|
-
"""mapping of gate names to information about the gates"""
|
|
541
|
-
|
|
542
|
-
@cached_property
|
|
543
|
-
def components(self) -> tuple[str, ...]:
|
|
544
|
-
"""All locus components (qubits and computational resonators) sorted.
|
|
545
|
-
|
|
546
|
-
The components are first sorted alphabetically based on their non-numeric part, and then
|
|
547
|
-
components with the same non-numeric part are sorted numerically. An example of components
|
|
548
|
-
sorted this way would be: ('COMPR1', 'COMPR2', 'QB1', 'QB2', 'QB3', 'QB10', 'QB11', 'QB20').
|
|
549
|
-
"""
|
|
550
|
-
return tuple(sorted(self.qubits + self.computational_resonators, key=_component_sort_key))
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
class HeraldingMode(str, Enum):
|
|
554
|
-
"""Heralding mode for circuit execution.
|
|
555
|
-
|
|
556
|
-
Heralding is the practice of generating data about the state of qubits prior to execution of a circuit.
|
|
557
|
-
This can be achieved by measuring the qubits immediately before executing each shot for a circuit.
|
|
558
|
-
"""
|
|
559
|
-
|
|
560
|
-
NONE = "none"
|
|
561
|
-
"""Do not do any heralding."""
|
|
562
|
-
ZEROS = "zeros"
|
|
563
|
-
"""Perform a heralding measurement after qubit initialization, only retain shots with an all-zeros result.
|
|
564
|
-
|
|
565
|
-
Note: in this mode, the number of shots returned after execution will be less or equal to the requested amount
|
|
566
|
-
due to the post-selection based on heralding data."""
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
class MoveGateValidationMode(str, Enum):
|
|
570
|
-
"""MOVE gate validation mode for circuit compilation. This options is meant for advanced users."""
|
|
571
|
-
|
|
572
|
-
STRICT = "strict"
|
|
573
|
-
"""Perform standard MOVE gate validation: MOVE gates must only appear in sandwiches, with no gates acting on the
|
|
574
|
-
MOVE qubit inside the sandwich."""
|
|
575
|
-
ALLOW_PRX = "allow_prx"
|
|
576
|
-
"""Allow PRX gates on the MOVE qubit inside MOVE sandwiches during validation."""
|
|
577
|
-
NONE = "none"
|
|
578
|
-
"""Do not perform any MOVE gate validation."""
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
class MoveGateFrameTrackingMode(str, Enum):
|
|
582
|
-
"""MOVE gate frame tracking mode for circuit compilation. This option is meant for advanced users."""
|
|
583
|
-
|
|
584
|
-
FULL = "full"
|
|
585
|
-
"""Perform complete MOVE gate frame tracking."""
|
|
586
|
-
NO_DETUNING_CORRECTION = "no_detuning_correction"
|
|
587
|
-
"""Do not add the phase detuning corrections to the pulse schedule for the MOVE gate. The user is expected to do
|
|
588
|
-
these manually."""
|
|
589
|
-
NONE = "none"
|
|
590
|
-
"""Do not perform any MOVE gate frame tracking. The user is expected to do these manually."""
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
class DDMode(str, Enum):
|
|
594
|
-
"""Dynamical Decoupling (DD) mode for circuit execution."""
|
|
595
|
-
|
|
596
|
-
DISABLED = "disabled"
|
|
597
|
-
"""Do not apply dynamical decoupling."""
|
|
598
|
-
ENABLED = "enabled"
|
|
599
|
-
"""Apply dynamical decoupling."""
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
PRXSequence = list[tuple[float, float]]
|
|
603
|
-
"""A sequence of PRX gates. A generic PRX gate is defined by rotation angle and phase angle, Theta and Phi,
|
|
604
|
-
respectively."""
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
class DDStrategy(BaseModel):
|
|
608
|
-
"""Describes a particular dynamical decoupling strategy.
|
|
609
|
-
|
|
610
|
-
The current standard DD stategy can be found in :attr:`.STANDARD_DD_STRATEGY`,
|
|
611
|
-
but users can use this class to provide their own dynamical decoupling strategies.
|
|
612
|
-
|
|
613
|
-
See Ezzell et al., Phys. Rev. Appl. 20, 064027 (2022) for information on DD sequences.
|
|
614
|
-
"""
|
|
615
|
-
|
|
616
|
-
merge_contiguous_waits: bool = Field(default=True)
|
|
617
|
-
"""Merge contiguous ``Wait`` instructions into one if they are separated only by ``Block`` instructions."""
|
|
618
|
-
|
|
619
|
-
target_qubits: frozenset[str] | None = Field(default=None)
|
|
620
|
-
"""Qubits on which dynamical decoupling should be applied. If ``None``, all qubits are targeted."""
|
|
621
|
-
|
|
622
|
-
skip_leading_wait: bool = Field(default=True)
|
|
623
|
-
"""Skip processing leading ``Wait`` instructions."""
|
|
624
|
-
|
|
625
|
-
skip_trailing_wait: bool = Field(default=True)
|
|
626
|
-
"""Skip processing trailing ``Wait`` instructions."""
|
|
627
|
-
|
|
628
|
-
gate_sequences: list[tuple[int, str | PRXSequence, str]] = Field(default_factory=list)
|
|
629
|
-
"""Available decoupling gate sequences to chose from in this strategy.
|
|
630
|
-
|
|
631
|
-
Each sequence is defined by a tuple of ``(ratio, gate pattern, align)``:
|
|
632
|
-
|
|
633
|
-
* ratio: Minimal duration for the sequence (in PRX gate durations) as integer.
|
|
634
|
-
|
|
635
|
-
* gate pattern: Gate pattern can be defined in two ways. It can be a string containing "X" and "Y" characters,
|
|
636
|
-
encoding a PRX gate sequence. For example, "YXYX" corresponds to the
|
|
637
|
-
XY4 sequence, "XYXYYXYX" to the EDD sequence, etc. If more flexibility is needed, a gate pattern can be
|
|
638
|
-
defined as a sequence of PRX gate argument tuples (that contain a rotation angle and a phase angle). For
|
|
639
|
-
example, sequence "YX" could be written as ``[(math.pi, math.pi / 2), (math.pi, 0)]``.
|
|
640
|
-
|
|
641
|
-
* align: Controls the alignment of the sequence within the time window it is inserted in. Supported values:
|
|
642
|
-
|
|
643
|
-
- "asap": Corresponds to a ASAP-aligned sequence with no waiting time before the first pulse.
|
|
644
|
-
- "center": Corresponds to a symmetric sequence.
|
|
645
|
-
- "alap": Corresponds to a ALAP-aligned sequence.
|
|
646
|
-
|
|
647
|
-
The Dynamical Decoupling algorithm uses the best fitting gate sequence by first sorting them
|
|
648
|
-
by ``ratio`` in descending order. Then the longest fitting pattern is determined by comparing ``ratio``
|
|
649
|
-
with the duration of the time window divided by the PRX gate duration.
|
|
650
|
-
"""
|
|
651
|
-
|
|
652
|
-
|
|
653
344
|
STANDARD_DD_STRATEGY = DDStrategy(gate_sequences=[(9, "XYXYYXYX", "asap"), (5, "YXYX", "asap"), (2, "XX", "center")])
|
|
654
345
|
"""The default DD strategy uses the following gate sequences:
|
|
655
346
|
|
|
@@ -700,295 +391,13 @@ class CircuitCompilationOptions:
|
|
|
700
391
|
)
|
|
701
392
|
|
|
702
393
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
Note: all circuits in a batch must measure the same qubits otherwise batch execution fails.
|
|
707
|
-
"""
|
|
708
|
-
|
|
709
|
-
circuits: CircuitBatch = Field(...)
|
|
710
|
-
"""batch of quantum circuit(s) to execute"""
|
|
711
|
-
custom_settings: dict[str, Any] | None = Field(None)
|
|
712
|
-
"""Custom settings to override default IQM hardware settings and calibration data.
|
|
713
|
-
Note: This field should be always None in normal use."""
|
|
714
|
-
calibration_set_id: UUID | None = Field(None)
|
|
715
|
-
"""ID of the calibration set to use, or None to use the latest calibration set"""
|
|
716
|
-
qubit_mapping: list[SingleQubitMapping] | None = Field(None)
|
|
717
|
-
"""mapping of logical qubit names to physical qubit names, or None if using physical qubit names"""
|
|
718
|
-
shots: int = Field(..., gt=0)
|
|
719
|
-
"""how many times to execute each circuit in the batch, must be greater than zero"""
|
|
720
|
-
max_circuit_duration_over_t2: float | None = Field(None)
|
|
721
|
-
"""Circuits are disqualified on the server if they are longer than this ratio
|
|
722
|
-
of the T2 time of the qubits.
|
|
723
|
-
If set to 0.0, no circuits are disqualified. If set to None the server default value is used."""
|
|
724
|
-
heralding_mode: HeraldingMode = Field(HeraldingMode.NONE)
|
|
725
|
-
"""which heralding mode to use during the execution of circuits in this request."""
|
|
726
|
-
|
|
727
|
-
move_validation_mode: MoveGateValidationMode = Field(MoveGateValidationMode.STRICT)
|
|
728
|
-
"""Which method of MOVE gate validation to use for circuit compilation."""
|
|
729
|
-
move_gate_frame_tracking_mode: MoveGateFrameTrackingMode = Field(MoveGateFrameTrackingMode.FULL)
|
|
730
|
-
"""Which method of MOVE gate frame tracking to use for circuit compilation."""
|
|
731
|
-
active_reset_cycles: int | None = Field(None)
|
|
732
|
-
"""Number of active ``reset`` operations inserted at the beginning of each circuit for each active qubit.
|
|
733
|
-
``None`` means active reset is not used but instead reset is done by waiting (relaxation). Integer values smaller
|
|
734
|
-
than 1 result in neither active nor reset by wait being used, in which case any reset operations must be explicitly
|
|
735
|
-
added in the circuit."""
|
|
736
|
-
dd_mode: DDMode = Field(DDMode.DISABLED)
|
|
737
|
-
"""Control whether dynamical decoupling should be enabled or disabled during the execution."""
|
|
738
|
-
dd_strategy: DDStrategy | None = Field(None)
|
|
739
|
-
"""A particular dynamical decoupling strategy to be used during the execution."""
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
CircuitMeasurementResults = dict[str, list[list[int]]]
|
|
743
|
-
"""Measurement results from a single circuit. For each measurement operation in the circuit,
|
|
744
|
-
maps the measurement key to the corresponding results. The outer list elements correspond to shots,
|
|
745
|
-
and the inner list elements to the qubits measured in the measurement operation."""
|
|
746
|
-
|
|
747
|
-
CircuitMeasurementResultsBatch = list[CircuitMeasurementResults]
|
|
748
|
-
"""Type that represents measurement results for a batch of circuits."""
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
class JobParameters(BaseModel):
|
|
752
|
-
"""Job-specific parameters extracted from the original RunRequest."""
|
|
753
|
-
|
|
754
|
-
shots: int = Field(...)
|
|
755
|
-
max_circuit_duration_over_t2: float | None = Field(None)
|
|
756
|
-
heralding_mode: HeraldingMode = Field(HeraldingMode.NONE)
|
|
757
|
-
move_validation_mode: MoveGateValidationMode = Field(MoveGateValidationMode.STRICT)
|
|
758
|
-
move_gate_frame_tracking_mode: MoveGateFrameTrackingMode = Field(MoveGateFrameTrackingMode.FULL)
|
|
759
|
-
dd_mode: DDMode = Field(DDMode.DISABLED)
|
|
760
|
-
dd_strategy: DDStrategy | None = Field(None)
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
class Metadata(BaseModel):
|
|
764
|
-
"""Metadata describing a circuit execution job."""
|
|
765
|
-
|
|
766
|
-
calibration_set_id: UUID | None = Field(None)
|
|
767
|
-
"""ID of the calibration set used"""
|
|
768
|
-
request: RunRequest | None = Field(None)
|
|
769
|
-
"""optional copy of the original RunRequest sent to the server"""
|
|
770
|
-
parameters: JobParameters | None = Field(None)
|
|
771
|
-
"""job-specific parameters extracted from the original request"""
|
|
772
|
-
circuits_batch: CircuitBatch | None = Field(None)
|
|
773
|
-
"""circuits batch submitted for execution"""
|
|
774
|
-
cocos_version: str | None = Field(None)
|
|
775
|
-
"""CoCoS version used to execute the job"""
|
|
776
|
-
timestamps: dict[str, str] | None = Field(None)
|
|
777
|
-
"""Timestamps of execution progress"""
|
|
778
|
-
|
|
779
|
-
@property
|
|
780
|
-
def shots(self) -> int:
|
|
781
|
-
"""Return the number of shots in the job."""
|
|
782
|
-
if self.parameters is not None:
|
|
783
|
-
return self.parameters.shots
|
|
784
|
-
if self.request is not None:
|
|
785
|
-
return self.request.shots
|
|
786
|
-
raise ValueError("No shots information available in the metadata")
|
|
787
|
-
|
|
788
|
-
@property
|
|
789
|
-
def circuits(self) -> CircuitBatch:
|
|
790
|
-
"""Return the circuits in the job."""
|
|
791
|
-
if self.circuits_batch is not None:
|
|
792
|
-
return self.circuits_batch
|
|
793
|
-
if self.request is not None:
|
|
794
|
-
return self.request.circuits
|
|
795
|
-
raise ValueError("No circuits information available in the metadata")
|
|
796
|
-
|
|
797
|
-
@property
|
|
798
|
-
def heralding_mode(self) -> HeraldingMode:
|
|
799
|
-
"""Return the heralding mode requested with the job."""
|
|
800
|
-
if self.parameters is not None:
|
|
801
|
-
return self.parameters.heralding_mode
|
|
802
|
-
if self.request is not None:
|
|
803
|
-
return self.request.heralding_mode
|
|
804
|
-
raise ValueError("No heralding mode information available in the metadata")
|
|
805
|
-
|
|
806
|
-
@property
|
|
807
|
-
def dd_mode(self) -> DDMode:
|
|
808
|
-
"""Return the dynamical decoupling mode requested with the job."""
|
|
809
|
-
if self.parameters is not None:
|
|
810
|
-
return self.parameters.dd_mode
|
|
811
|
-
if self.request is not None:
|
|
812
|
-
return self.request.dd_mode
|
|
813
|
-
raise ValueError("No dynamical decoupling mode information available in the metadata")
|
|
814
|
-
|
|
815
|
-
@property
|
|
816
|
-
def dd_strategy(self) -> DDStrategy | None:
|
|
817
|
-
"""Return the dynamical decoupling strategy used with the job."""
|
|
818
|
-
if self.parameters is not None:
|
|
819
|
-
return self.parameters.dd_strategy
|
|
820
|
-
if self.request is not None:
|
|
821
|
-
return self.request.dd_strategy
|
|
822
|
-
raise ValueError("No dynamical decoupling strategy information available in the metadata")
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
class Status(str, Enum):
|
|
826
|
-
"""Status of a job."""
|
|
827
|
-
|
|
828
|
-
RECEIVED = "received"
|
|
829
|
-
"""Job has been received but nothing has been done about it."""
|
|
830
|
-
VALIDATION_STARTED = "validation_started"
|
|
831
|
-
"""Job is being validated."""
|
|
832
|
-
PROCESSING = "processing"
|
|
833
|
-
VALIDATION_ENDED = "validation_ended"
|
|
834
|
-
"""Job has passed initial checks and will proceed to compilation."""
|
|
835
|
-
FETCH_CALIBRATION_STARTED = "fetch_calibration_started"
|
|
836
|
-
"""The calibration is being fetched for the job."""
|
|
837
|
-
FETCH_CALIBRATION_ENDED = "fetch_calibration_ended"
|
|
838
|
-
"""Job has been fetched for the job."""
|
|
839
|
-
COMPILATION_STARTED = "compilation_started"
|
|
840
|
-
"""The job is being compiled."""
|
|
841
|
-
COMPILATION_ENDED = "compilation_ended"
|
|
842
|
-
"""Job has been compiled to a low-level representation."""
|
|
843
|
-
SAVE_SWEEP_METADATA_STARTED = "save_sweep_metadata_started"
|
|
844
|
-
"""Metadata about the sweep is being saved to the database."""
|
|
845
|
-
SAVE_SWEEP_METADATA_ENDED = "save_sweep_metadata_ended"
|
|
846
|
-
"""Metadata has been successfully saved."""
|
|
847
|
-
PENDING_EXECUTION = "pending_execution"
|
|
848
|
-
"""Job has been compiled and is queued for execution."""
|
|
849
|
-
EXECUTION_STARTED = "execution_started"
|
|
850
|
-
"""The job has begun execution on hardware."""
|
|
851
|
-
EXECUTION_ENDED = "execution_ended"
|
|
852
|
-
"""The job has completed execution on hardware."""
|
|
853
|
-
POST_PROCESSING_PENDING = "post_processing_pending"
|
|
854
|
-
"""The job has finished executing and is pending post-processing."""
|
|
855
|
-
POST_PROCESSING_STARTED = "post_processing_started"
|
|
856
|
-
"""The job results is being post-processed."""
|
|
857
|
-
POST_PROCESSING_ENDED = "post_processing_ended"
|
|
858
|
-
"""The job has been successfully post processed."""
|
|
859
|
-
READY = "ready"
|
|
860
|
-
"""Job has been executed and results are available."""
|
|
861
|
-
FAILED = "failed"
|
|
862
|
-
"""Execution or compilation failed."""
|
|
863
|
-
ABORTED = "aborted"
|
|
864
|
-
"""User cancelled the execution."""
|
|
865
|
-
PENDING_DELETION = "pending deletion"
|
|
866
|
-
"""Job is set to be deleted."""
|
|
867
|
-
DELETION_FAILED = "deletion failed"
|
|
868
|
-
"""Job was supposed to be deleted but deletion failed."""
|
|
869
|
-
DELETED = "deleted"
|
|
870
|
-
"""Job deleted from the database."""
|
|
871
|
-
UNKNOWN = "unknown"
|
|
872
|
-
"""Job is in a state not recognized by this version of the client."""
|
|
873
|
-
|
|
874
|
-
@classmethod
|
|
875
|
-
def _missing_(cls, value: object) -> Any:
|
|
876
|
-
"""This is a backwards compatibility fix for resonance integration. Once Resonance has fixed this, and has been
|
|
877
|
-
widely deployed, this can be removed. Ticket: QCLOUD-1311
|
|
878
|
-
"""
|
|
879
|
-
if value == "pending execution":
|
|
880
|
-
return cls.PENDING_EXECUTION
|
|
881
|
-
|
|
882
|
-
@classmethod
|
|
883
|
-
def terminal_statuses(cls) -> set[Status]:
|
|
884
|
-
"""Statuses from which the execution can't continue."""
|
|
885
|
-
return {cls.READY, cls.FAILED, cls.ABORTED, cls.DELETED, cls.DELETION_FAILED}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
class RunResult(BaseModel):
|
|
889
|
-
"""Results of the quantum circuit execution job.
|
|
890
|
-
If the job succeeded, :attr:`measurements` contains the output of the batch of circuits,
|
|
891
|
-
consisting of the results of the measurement operations in each circuit.
|
|
892
|
-
It is a list of dictionaries, where each dict maps each measurement key to a 2D array of measurement
|
|
893
|
-
results, represented as a nested list.
|
|
894
|
-
``RunResult.measurements[circuit_index][key][shot][qubit_index]`` is the result of measuring the
|
|
895
|
-
``qubit_index``'th qubit in measurement operation ``key`` in the shot ``shot`` in the
|
|
896
|
-
``circuit_index``'th circuit of the batch.
|
|
897
|
-
:attr:`measurements` is present iff the status is ``'ready'``.
|
|
898
|
-
:attr:`message` carries additional information for the ``'failed'`` status.
|
|
899
|
-
If the status is ``'pending compilation'`` or ``'pending execution'``,
|
|
900
|
-
:attr:`measurements` and :attr:`message` are ``None``.
|
|
901
|
-
|
|
902
|
-
The results are non-negative integers representing the computational basis state (for qubits, 0 or 1)
|
|
903
|
-
that was the measurement outcome.
|
|
904
|
-
|
|
905
|
-
----
|
|
906
|
-
"""
|
|
907
|
-
|
|
908
|
-
status: Status = Field(...)
|
|
909
|
-
"""current status of the job, in ``{'pending_compilation', 'pending_execution', 'ready', 'failed', 'aborted'}``"""
|
|
910
|
-
measurements: CircuitMeasurementResultsBatch | None = Field(None)
|
|
911
|
-
"""if the job has finished successfully, the measurement results for the circuit(s)"""
|
|
912
|
-
message: str | None = Field(None)
|
|
913
|
-
"""if the job failed, an error message"""
|
|
914
|
-
metadata: Metadata = Field(...)
|
|
915
|
-
"""metadata about the job"""
|
|
916
|
-
warnings: list[str] | None = Field(None)
|
|
917
|
-
"""list of warning messages"""
|
|
918
|
-
|
|
919
|
-
@staticmethod
|
|
920
|
-
def from_dict(inp: dict[str, str | dict | list | None]) -> RunResult:
|
|
921
|
-
"""Parses the result from a dict.
|
|
922
|
-
|
|
923
|
-
Args:
|
|
924
|
-
inp: value to parse, has to map to RunResult
|
|
925
|
-
|
|
926
|
-
Returns:
|
|
927
|
-
parsed job result
|
|
928
|
-
|
|
929
|
-
"""
|
|
930
|
-
input_copy = inp.copy()
|
|
931
|
-
try:
|
|
932
|
-
status = Status(input_copy.pop("status"))
|
|
933
|
-
except ValueError:
|
|
934
|
-
status = Status.UNKNOWN
|
|
935
|
-
return RunResult(status=status, **input_copy) # type:ignore[arg-type]
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
class RunStatus(BaseModel):
|
|
939
|
-
"""Status of a circuit execution job."""
|
|
940
|
-
|
|
941
|
-
status: Status = Field(...)
|
|
942
|
-
"""current status of the job, in ``{'pending_compilation', 'pending_execution', 'ready', 'failed', 'aborted'}``"""
|
|
943
|
-
message: str | None = Field(None)
|
|
944
|
-
"""if the job failed, an error message"""
|
|
945
|
-
warnings: list[str] | None = Field(None)
|
|
946
|
-
"""list of warning messages"""
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
class Counts(BaseModel):
|
|
950
|
-
"""Circuit measurement results in histogram representation."""
|
|
951
|
-
|
|
952
|
-
measurement_keys: list[str]
|
|
953
|
-
"""Measurement keys in the order they are concatenated to form the state bitstrings in :attr:`counts`.
|
|
954
|
-
|
|
955
|
-
For example, if :attr:`measurement_keys` is ``['mk_1', 'mk2']`` and ``'mk_1'`` measures ``QB1``
|
|
956
|
-
and ``'mk_2'`` measures ``QB3`` and ``QB5``, then :attr:`counts` could contains keys such as ``'010'`` representing
|
|
957
|
-
shots where ``QB1`, ``QB3`` and ``QB5`` were observed to be in the state :math:`|010\rangle`.
|
|
958
|
-
"""
|
|
959
|
-
counts: dict[str, int]
|
|
960
|
-
"""mapping from computational basis states, represented as bitstrings, to the number of times they were observed
|
|
961
|
-
when executing the circuit"""
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
class RunCounts(BaseModel):
|
|
965
|
-
"""Measurement results of a circuit execution job in histogram representation."""
|
|
966
|
-
|
|
967
|
-
status: Status = Field(...)
|
|
968
|
-
"""current status of the job, in ``{'pending compilation', 'pending execution', 'ready', 'failed', 'aborted'}``"""
|
|
969
|
-
counts_batch: list[Counts] | None = Field(None)
|
|
970
|
-
"""measurement results in histogram representation for each circuit in the batch"""
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
class ClientLibrary(BaseModel):
|
|
974
|
-
"""Represents a client library with its metadata.
|
|
975
|
-
|
|
976
|
-
Args:
|
|
977
|
-
name: display name of the client library.
|
|
978
|
-
package_name: name of the package as published in package repositories.
|
|
979
|
-
repo_url: URL to the source code repository.
|
|
980
|
-
package_url: URL to the package in the package repository.
|
|
981
|
-
min: minimum supported version.
|
|
982
|
-
max: maximum supported version.
|
|
983
|
-
|
|
984
|
-
"""
|
|
985
|
-
|
|
986
|
-
name: str
|
|
987
|
-
package_name: str | None = Field(None)
|
|
988
|
-
repo_url: str | None = Field(None)
|
|
989
|
-
package_url: str | None = Field(None)
|
|
990
|
-
min: str
|
|
991
|
-
max: str
|
|
992
|
-
|
|
394
|
+
@dataclass(frozen=True, kw_only=True)
|
|
395
|
+
class CircuitJobParameters(CircuitCompilationOptions):
|
|
396
|
+
"""Parameters for a circuit execution job (see :class:`RunRequest` for definitions)."""
|
|
993
397
|
|
|
994
|
-
|
|
398
|
+
shots: int
|
|
399
|
+
"""How many times to execute each circuit in the batch, must be greater than zero."""
|
|
400
|
+
calibration_set_id: UUID | None = None
|
|
401
|
+
"""ID of the calibration set to use, or None to use the current default calibration set."""
|
|
402
|
+
qubit_mapping: QubitMapping | None = None
|
|
403
|
+
"""Mapping of logical qubit names to physical qubit names, or None if ``circuits`` use physical qubit names."""
|