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.
Files changed (45) hide show
  1. iqm/cirq_iqm/devices/iqm_device_metadata.py +2 -1
  2. iqm/cirq_iqm/examples/demo_common.py +1 -1
  3. iqm/cirq_iqm/examples/demo_iqm_execution.py +3 -3
  4. iqm/cirq_iqm/iqm_sampler.py +47 -29
  5. iqm/cirq_iqm/serialize.py +1 -1
  6. iqm/cirq_iqm/transpiler.py +3 -1
  7. iqm/iqm_client/__init__.py +0 -2
  8. iqm/iqm_client/errors.py +6 -17
  9. iqm/iqm_client/iqm_client.py +199 -602
  10. iqm/iqm_client/models.py +20 -611
  11. iqm/iqm_client/transpile.py +11 -8
  12. iqm/iqm_client/validation.py +18 -9
  13. iqm/iqm_server_client/__init__.py +14 -0
  14. iqm/iqm_server_client/errors.py +6 -0
  15. iqm/iqm_server_client/iqm_server_client.py +755 -0
  16. iqm/iqm_server_client/models.py +179 -0
  17. iqm/iqm_server_client/py.typed +0 -0
  18. iqm/qiskit_iqm/__init__.py +8 -0
  19. iqm/qiskit_iqm/examples/bell_measure.py +2 -2
  20. iqm/qiskit_iqm/examples/transpile_example.py +9 -4
  21. iqm/qiskit_iqm/fake_backends/fake_adonis.py +2 -1
  22. iqm/qiskit_iqm/fake_backends/fake_aphrodite.py +2 -1
  23. iqm/qiskit_iqm/fake_backends/fake_apollo.py +2 -1
  24. iqm/qiskit_iqm/fake_backends/fake_deneb.py +2 -1
  25. iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py +8 -7
  26. iqm/qiskit_iqm/iqm_backend.py +3 -4
  27. iqm/qiskit_iqm/iqm_circuit_validation.py +8 -7
  28. iqm/qiskit_iqm/iqm_job.py +106 -88
  29. iqm/qiskit_iqm/iqm_move_layout.py +2 -1
  30. iqm/qiskit_iqm/iqm_naive_move_pass.py +115 -56
  31. iqm/qiskit_iqm/iqm_provider.py +49 -36
  32. iqm/qiskit_iqm/iqm_target.py +12 -8
  33. iqm/qiskit_iqm/iqm_transpilation.py +219 -26
  34. iqm/qiskit_iqm/qiskit_to_iqm.py +150 -41
  35. iqm/qiskit_iqm/transpiler_plugins.py +11 -8
  36. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/METADATA +4 -14
  37. iqm_client-33.0.0.dist-info/RECORD +63 -0
  38. iqm/iqm_client/api.py +0 -90
  39. iqm/iqm_client/authentication.py +0 -206
  40. iqm_client-32.0.0.dist-info/RECORD +0 -60
  41. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/AUTHORS.rst +0 -0
  42. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/LICENSE.txt +0 -0
  43. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/WHEEL +0 -0
  44. {iqm_client-32.0.0.dist-info → iqm_client-33.0.0.dist-info}/entry_points.txt +0 -0
  45. {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"""This module contains the data models used by IQMClient.
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, StrictStr, TypeAdapter, field_validator
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
- class RunRequest(BaseModel):
704
- """Request for an IQM quantum computer to run a job that executes a batch of quantum circuits.
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
- ClientLibraryDict = TypeAdapter(dict[str, ClientLibrary])
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."""