qoro-divi 0.2.0b1__py3-none-any.whl → 0.6.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.
- divi/__init__.py +1 -2
- divi/backends/__init__.py +10 -0
- divi/backends/_backend_properties_conversion.py +227 -0
- divi/backends/_circuit_runner.py +70 -0
- divi/backends/_execution_result.py +70 -0
- divi/backends/_parallel_simulator.py +486 -0
- divi/backends/_qoro_service.py +663 -0
- divi/backends/_qpu_system.py +101 -0
- divi/backends/_results_processing.py +133 -0
- divi/circuits/__init__.py +13 -0
- divi/{exp/cirq → circuits/_cirq}/__init__.py +1 -2
- divi/circuits/_cirq/_parser.py +110 -0
- divi/circuits/_cirq/_qasm_export.py +78 -0
- divi/circuits/_core.py +391 -0
- divi/{qasm.py → circuits/_qasm_conversion.py} +73 -14
- divi/circuits/_qasm_validation.py +694 -0
- divi/qprog/__init__.py +27 -8
- divi/qprog/_expectation.py +181 -0
- divi/qprog/_hamiltonians.py +281 -0
- divi/qprog/algorithms/__init__.py +16 -0
- divi/qprog/algorithms/_ansatze.py +368 -0
- divi/qprog/algorithms/_custom_vqa.py +263 -0
- divi/qprog/algorithms/_pce.py +262 -0
- divi/qprog/algorithms/_qaoa.py +579 -0
- divi/qprog/algorithms/_vqe.py +262 -0
- divi/qprog/batch.py +387 -74
- divi/qprog/checkpointing.py +556 -0
- divi/qprog/exceptions.py +9 -0
- divi/qprog/optimizers.py +1014 -43
- divi/qprog/quantum_program.py +243 -412
- divi/qprog/typing.py +62 -0
- divi/qprog/variational_quantum_algorithm.py +1208 -0
- divi/qprog/workflows/__init__.py +10 -0
- divi/qprog/{_graph_partitioning.py → workflows/_graph_partitioning.py} +139 -95
- divi/qprog/workflows/_qubo_partitioning.py +221 -0
- divi/qprog/workflows/_vqe_sweep.py +560 -0
- divi/reporting/__init__.py +7 -0
- divi/reporting/_pbar.py +127 -0
- divi/reporting/_qlogger.py +68 -0
- divi/reporting/_reporter.py +155 -0
- {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.6.0.dist-info}/METADATA +43 -15
- qoro_divi-0.6.0.dist-info/RECORD +47 -0
- {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.6.0.dist-info}/WHEEL +1 -1
- qoro_divi-0.6.0.dist-info/licenses/LICENSES/.license-header +3 -0
- divi/_pbar.py +0 -73
- divi/circuits.py +0 -139
- divi/exp/cirq/_lexer.py +0 -126
- divi/exp/cirq/_parser.py +0 -889
- divi/exp/cirq/_qasm_export.py +0 -37
- divi/exp/cirq/_qasm_import.py +0 -35
- divi/exp/cirq/exception.py +0 -21
- divi/exp/scipy/_cobyla.py +0 -342
- divi/exp/scipy/pyprima/LICENCE.txt +0 -28
- divi/exp/scipy/pyprima/__init__.py +0 -263
- divi/exp/scipy/pyprima/cobyla/__init__.py +0 -0
- divi/exp/scipy/pyprima/cobyla/cobyla.py +0 -599
- divi/exp/scipy/pyprima/cobyla/cobylb.py +0 -849
- divi/exp/scipy/pyprima/cobyla/geometry.py +0 -240
- divi/exp/scipy/pyprima/cobyla/initialize.py +0 -269
- divi/exp/scipy/pyprima/cobyla/trustregion.py +0 -540
- divi/exp/scipy/pyprima/cobyla/update.py +0 -331
- divi/exp/scipy/pyprima/common/__init__.py +0 -0
- divi/exp/scipy/pyprima/common/_bounds.py +0 -41
- divi/exp/scipy/pyprima/common/_linear_constraints.py +0 -46
- divi/exp/scipy/pyprima/common/_nonlinear_constraints.py +0 -64
- divi/exp/scipy/pyprima/common/_project.py +0 -224
- divi/exp/scipy/pyprima/common/checkbreak.py +0 -107
- divi/exp/scipy/pyprima/common/consts.py +0 -48
- divi/exp/scipy/pyprima/common/evaluate.py +0 -101
- divi/exp/scipy/pyprima/common/history.py +0 -39
- divi/exp/scipy/pyprima/common/infos.py +0 -30
- divi/exp/scipy/pyprima/common/linalg.py +0 -452
- divi/exp/scipy/pyprima/common/message.py +0 -336
- divi/exp/scipy/pyprima/common/powalg.py +0 -131
- divi/exp/scipy/pyprima/common/preproc.py +0 -393
- divi/exp/scipy/pyprima/common/present.py +0 -5
- divi/exp/scipy/pyprima/common/ratio.py +0 -56
- divi/exp/scipy/pyprima/common/redrho.py +0 -49
- divi/exp/scipy/pyprima/common/selectx.py +0 -346
- divi/interfaces.py +0 -25
- divi/parallel_simulator.py +0 -258
- divi/qlogger.py +0 -119
- divi/qoro_service.py +0 -343
- divi/qprog/_mlae.py +0 -182
- divi/qprog/_qaoa.py +0 -440
- divi/qprog/_vqe.py +0 -275
- divi/qprog/_vqe_sweep.py +0 -144
- divi/utils.py +0 -116
- qoro_divi-0.2.0b1.dist-info/RECORD +0 -58
- /divi/{qem.py → circuits/qem.py} +0 -0
- {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.6.0.dist-info/licenses}/LICENSE +0 -0
- {qoro_divi-0.2.0b1.dist-info → qoro_divi-0.6.0.dist-info/licenses}/LICENSES/Apache-2.0.txt +0 -0
divi/__init__.py
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025-2026 Qoro Quantum Ltd <divi@qoroquantum.de>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
from ._backend_properties_conversion import create_backend_from_properties
|
|
6
|
+
from ._circuit_runner import CircuitRunner
|
|
7
|
+
from ._execution_result import ExecutionResult
|
|
8
|
+
from ._parallel_simulator import ParallelSimulator
|
|
9
|
+
from ._qoro_service import JobConfig, JobStatus, JobType, QoroService
|
|
10
|
+
from ._results_processing import convert_counts_to_probs, reverse_dict_endianness
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025-2026 Qoro Quantum Ltd <divi@qoroquantum.de>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""Utilities for working with Qiskit BackendProperties and BackendV2 conversion."""
|
|
6
|
+
|
|
7
|
+
import datetime
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from qiskit.providers.fake_provider import GenericBackendV2
|
|
11
|
+
from qiskit_ibm_runtime.models.backend_properties import BackendProperties
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _normalize_properties(
|
|
15
|
+
properties: dict[str, Any],
|
|
16
|
+
default_date: datetime.datetime | None = None,
|
|
17
|
+
) -> dict[str, Any]:
|
|
18
|
+
"""
|
|
19
|
+
Preprocess an incomplete BackendProperties dictionary by filling in missing
|
|
20
|
+
required fields with sensible defaults.
|
|
21
|
+
|
|
22
|
+
This function makes it easier to create BackendProperties dictionaries by
|
|
23
|
+
allowing you to omit fields that have obvious defaults, such as:
|
|
24
|
+
- Missing top-level fields: `backend_name`, `backend_version`, `last_update_date`
|
|
25
|
+
- Missing `unit` field for dimensionless parameters (e.g., gate_error)
|
|
26
|
+
- Missing `general` field (empty list)
|
|
27
|
+
- Missing `gates` field (empty list)
|
|
28
|
+
- Missing `qubits` field (empty list)
|
|
29
|
+
- Missing `date` fields in Nduv objects
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
properties: Incomplete BackendProperties dictionary. Can omit:
|
|
33
|
+
- `unit` field in parameter/qubit Nduv objects (defaults to "" for
|
|
34
|
+
dimensionless quantities like gate_error, or inferred from name)
|
|
35
|
+
- `general` field (defaults to empty list)
|
|
36
|
+
- `gates` field (defaults to empty list)
|
|
37
|
+
- `qubits` field (defaults to empty list)
|
|
38
|
+
- `date` field in Nduv objects (defaults to current time or provided default)
|
|
39
|
+
default_date: Optional datetime to use for missing date fields.
|
|
40
|
+
If None, uses current time.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Complete BackendProperties dictionary ready for BackendProperties.from_dict()
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
>>> props = {
|
|
47
|
+
... "backend_name": "test",
|
|
48
|
+
... "gates": [{
|
|
49
|
+
... "gate": "sx",
|
|
50
|
+
... "qubits": [0],
|
|
51
|
+
... "parameters": [{
|
|
52
|
+
... "name": "gate_error",
|
|
53
|
+
... "value": 0.01,
|
|
54
|
+
... # unit and date will be added automatically
|
|
55
|
+
... }]
|
|
56
|
+
... }]
|
|
57
|
+
... }
|
|
58
|
+
>>> normalized = _normalize_properties(props)
|
|
59
|
+
>>> backend_props = BackendProperties.from_dict(normalized)
|
|
60
|
+
"""
|
|
61
|
+
if default_date is None:
|
|
62
|
+
default_date = datetime.datetime.now()
|
|
63
|
+
|
|
64
|
+
# Create a shallow copy to avoid mutating the input
|
|
65
|
+
# (nested structures are rebuilt below to ensure no mutation)
|
|
66
|
+
normalized = properties.copy()
|
|
67
|
+
|
|
68
|
+
# Add missing required top-level fields
|
|
69
|
+
if "backend_name" not in normalized:
|
|
70
|
+
normalized["backend_name"] = "custom_backend"
|
|
71
|
+
if "backend_version" not in normalized:
|
|
72
|
+
normalized["backend_version"] = "1.0.0"
|
|
73
|
+
if "last_update_date" not in normalized:
|
|
74
|
+
normalized["last_update_date"] = default_date
|
|
75
|
+
|
|
76
|
+
# Add missing general field
|
|
77
|
+
if "general" not in normalized:
|
|
78
|
+
normalized["general"] = []
|
|
79
|
+
|
|
80
|
+
# Add missing gates field (required by BackendProperties)
|
|
81
|
+
if "gates" not in normalized:
|
|
82
|
+
normalized["gates"] = []
|
|
83
|
+
|
|
84
|
+
# Add missing qubits field (required by BackendProperties)
|
|
85
|
+
if "qubits" not in normalized:
|
|
86
|
+
normalized["qubits"] = []
|
|
87
|
+
|
|
88
|
+
# Normalize qubits (list of lists of Nduv objects)
|
|
89
|
+
if "qubits" in normalized:
|
|
90
|
+
normalized["qubits"] = [
|
|
91
|
+
[_normalize_nduv(param, default_date) for param in qubit_params]
|
|
92
|
+
for qubit_params in normalized["qubits"]
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
# Normalize gates (list of gate dicts with parameters)
|
|
96
|
+
if "gates" in normalized:
|
|
97
|
+
normalized["gates"] = [
|
|
98
|
+
{
|
|
99
|
+
**gate,
|
|
100
|
+
"parameters": [
|
|
101
|
+
_normalize_nduv(param, default_date)
|
|
102
|
+
for param in gate.get("parameters", [])
|
|
103
|
+
],
|
|
104
|
+
}
|
|
105
|
+
for gate in normalized["gates"]
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
# Normalize general (list of Nduv objects)
|
|
109
|
+
if "general" in normalized and normalized["general"]:
|
|
110
|
+
normalized["general"] = [
|
|
111
|
+
_normalize_nduv(param, default_date) for param in normalized["general"]
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
return normalized
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _normalize_nduv(
|
|
118
|
+
nduv: dict[str, Any], default_date: datetime.datetime
|
|
119
|
+
) -> dict[str, Any]:
|
|
120
|
+
"""
|
|
121
|
+
Normalize a single Nduv (Name, Date, Unit, Value) object by adding
|
|
122
|
+
missing required fields.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
nduv: Nduv dictionary (may be incomplete)
|
|
126
|
+
default_date: Default date to use if missing
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Complete Nduv dictionary
|
|
130
|
+
"""
|
|
131
|
+
normalized = nduv.copy()
|
|
132
|
+
|
|
133
|
+
# Add missing date field
|
|
134
|
+
if "date" not in normalized:
|
|
135
|
+
normalized["date"] = default_date
|
|
136
|
+
|
|
137
|
+
# Add missing unit field
|
|
138
|
+
if "unit" not in normalized:
|
|
139
|
+
name = normalized.get("name", "").lower()
|
|
140
|
+
# Dimensionless quantities
|
|
141
|
+
if name in ("gate_error", "readout_error", "prob"):
|
|
142
|
+
normalized["unit"] = ""
|
|
143
|
+
# Time-based quantities
|
|
144
|
+
elif name in ("t1", "t2", "gate_length", "readout_length"):
|
|
145
|
+
# Infer unit from common patterns, default to "ns" for gate_length
|
|
146
|
+
if name == "gate_length":
|
|
147
|
+
normalized["unit"] = "ns"
|
|
148
|
+
elif name in ("t1", "t2"):
|
|
149
|
+
normalized["unit"] = "us" # microseconds is common
|
|
150
|
+
else:
|
|
151
|
+
normalized["unit"] = "ns"
|
|
152
|
+
# Frequency-based quantities
|
|
153
|
+
elif name in ("frequency", "freq"):
|
|
154
|
+
normalized["unit"] = "GHz"
|
|
155
|
+
# Default to empty string for unknown quantities
|
|
156
|
+
else:
|
|
157
|
+
normalized["unit"] = ""
|
|
158
|
+
|
|
159
|
+
return normalized
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def create_backend_from_properties(
|
|
163
|
+
properties: dict[str, Any],
|
|
164
|
+
n_qubits: int | None = None,
|
|
165
|
+
default_date: datetime.datetime | None = None,
|
|
166
|
+
) -> GenericBackendV2:
|
|
167
|
+
"""
|
|
168
|
+
Create a populated GenericBackendV2 from a BackendProperties dictionary.
|
|
169
|
+
|
|
170
|
+
This function handles the complete workflow:
|
|
171
|
+
1. Normalizes the properties dictionary (fills in missing fields)
|
|
172
|
+
2. Infers the number of qubits from the properties if not provided
|
|
173
|
+
3. Creates a GenericBackendV2 backend
|
|
174
|
+
4. Populates it with the normalized properties
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
properties: BackendProperties dictionary.
|
|
178
|
+
Missing fields will be filled automatically.
|
|
179
|
+
n_qubits: Optional number of qubits. If None, will be inferred from the
|
|
180
|
+
length of the "qubits" list in the properties dictionary.
|
|
181
|
+
default_date: Optional datetime to use for missing date fields.
|
|
182
|
+
If None, uses current time.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
GenericBackendV2 backend populated with the provided properties.
|
|
186
|
+
|
|
187
|
+
Raises:
|
|
188
|
+
ValueError: If n_qubits is not provided and cannot be inferred from properties
|
|
189
|
+
(i.e., qubits list is empty or missing), or if n_qubits is less than 1.
|
|
190
|
+
|
|
191
|
+
Example:
|
|
192
|
+
>>> props = {
|
|
193
|
+
... "backend_name": "test",
|
|
194
|
+
... "qubits": [[{"name": "T1", "value": 100.0}]], # 1 qubit
|
|
195
|
+
... "gates": [{"gate": "sx", "qubits": [0], "parameters": []}]
|
|
196
|
+
... }
|
|
197
|
+
>>> # Infer qubit count from properties (will be 1)
|
|
198
|
+
>>> backend = create_backend_from_properties(props)
|
|
199
|
+
>>> backend.n_qubits
|
|
200
|
+
1
|
|
201
|
+
>>> # Override qubit count if needed
|
|
202
|
+
>>> backend_large = create_backend_from_properties(props, n_qubits=120)
|
|
203
|
+
>>> backend_large.n_qubits
|
|
204
|
+
120
|
|
205
|
+
"""
|
|
206
|
+
# Normalize the properties first
|
|
207
|
+
normalized_properties = _normalize_properties(properties, default_date)
|
|
208
|
+
|
|
209
|
+
# Infer number of qubits from qubits list length if not provided
|
|
210
|
+
if n_qubits is None:
|
|
211
|
+
n_qubits = len(normalized_properties.get("qubits", []))
|
|
212
|
+
if n_qubits == 0:
|
|
213
|
+
raise ValueError(
|
|
214
|
+
"n_qubits must be provided when properties dictionary has no qubits, "
|
|
215
|
+
"or qubits list must contain at least one qubit"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if n_qubits < 1:
|
|
219
|
+
raise ValueError("n_qubits must be at least 1")
|
|
220
|
+
|
|
221
|
+
# Create the backend
|
|
222
|
+
backend = GenericBackendV2(num_qubits=n_qubits)
|
|
223
|
+
|
|
224
|
+
# Populate with properties
|
|
225
|
+
backend._properties = BackendProperties.from_dict(normalized_properties)
|
|
226
|
+
|
|
227
|
+
return backend
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
from divi.backends._execution_result import ExecutionResult
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CircuitRunner(ABC):
|
|
11
|
+
"""
|
|
12
|
+
A generic interface for anything that can "run" quantum circuits.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, shots: int):
|
|
16
|
+
if shots <= 0:
|
|
17
|
+
raise ValueError(f"Shots must be a positive integer. Got {shots}.")
|
|
18
|
+
|
|
19
|
+
self._shots = shots
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def shots(self):
|
|
23
|
+
"""
|
|
24
|
+
Get the number of measurement shots for circuit execution.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
int: Number of shots configured for this runner.
|
|
28
|
+
"""
|
|
29
|
+
return self._shots
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def supports_expval(self) -> bool:
|
|
34
|
+
"""
|
|
35
|
+
Whether the backend supports expectation value measurements.
|
|
36
|
+
"""
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def is_async(self) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Whether the backend executes circuits asynchronously.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
bool: True if the backend returns a job ID and requires polling
|
|
47
|
+
for results (e.g., QoroService). False if the backend
|
|
48
|
+
returns results immediately (e.g., ParallelSimulator).
|
|
49
|
+
"""
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def submit_circuits(self, circuits: dict[str, str], **kwargs) -> ExecutionResult:
|
|
54
|
+
"""
|
|
55
|
+
Submit quantum circuits for execution.
|
|
56
|
+
|
|
57
|
+
This abstract method must be implemented by subclasses to define how
|
|
58
|
+
circuits are executed on their respective backends (simulator, hardware, etc.).
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
circuits (dict[str, str]): Dictionary mapping circuit labels to their
|
|
62
|
+
OpenQASM string representations.
|
|
63
|
+
**kwargs: Additional backend-specific parameters for circuit execution.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
ExecutionResult: For synchronous backends, contains results directly.
|
|
67
|
+
For asynchronous backends, contains a job_id that can be used to
|
|
68
|
+
fetch results later.
|
|
69
|
+
"""
|
|
70
|
+
pass
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Qoro Quantum Ltd <divi@qoroquantum.de>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, replace
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class ExecutionResult:
|
|
10
|
+
"""Result container for circuit execution.
|
|
11
|
+
|
|
12
|
+
This class provides a unified return type for all CircuitRunner.submit_circuits()
|
|
13
|
+
methods. For synchronous backends, it contains the results directly. For
|
|
14
|
+
asynchronous backends, it contains the job_id that can be used to fetch results later.
|
|
15
|
+
|
|
16
|
+
The class is frozen (immutable) to ensure data integrity. Use the `with_results()`
|
|
17
|
+
method to create a new instance with results populated from an async ExecutionResult.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
results (list[dict] | None): For sync backends or after fetching: List of result
|
|
21
|
+
dictionaries, each containing 'label' and 'results' keys. Format:
|
|
22
|
+
[{"label": str, "results": dict}, ...]
|
|
23
|
+
job_id (str | None): For async backends: Job identifier that can be used
|
|
24
|
+
to poll and retrieve results from the backend.
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
>>> # Synchronous backend
|
|
28
|
+
>>> result = ExecutionResult(results=[{"label": "circuit_0", "results": {"00": 100}}])
|
|
29
|
+
>>> result.is_async()
|
|
30
|
+
False
|
|
31
|
+
|
|
32
|
+
>>> # Asynchronous backend
|
|
33
|
+
>>> result = ExecutionResult(job_id="job-12345")
|
|
34
|
+
>>> result.is_async()
|
|
35
|
+
True
|
|
36
|
+
>>> # After fetching results
|
|
37
|
+
>>> result = backend.get_job_results(result)
|
|
38
|
+
>>> result.results is not None
|
|
39
|
+
True
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
results: list[dict] | None = None
|
|
43
|
+
"""Results for synchronous backends: [{"label": str, "results": dict}, ...]"""
|
|
44
|
+
|
|
45
|
+
job_id: str | None = None
|
|
46
|
+
"""Job identifier for asynchronous backends."""
|
|
47
|
+
|
|
48
|
+
def is_async(self) -> bool:
|
|
49
|
+
"""Check if this result represents an async job.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
bool: True if job_id is not None and results are None (async backend),
|
|
53
|
+
False otherwise (sync backend or results already fetched).
|
|
54
|
+
"""
|
|
55
|
+
return self.job_id is not None and self.results is None
|
|
56
|
+
|
|
57
|
+
def with_results(self, results: list[dict]) -> "ExecutionResult":
|
|
58
|
+
"""Create a new ExecutionResult with results populated.
|
|
59
|
+
|
|
60
|
+
This method creates a new instance with results set, effectively converting
|
|
61
|
+
an async ExecutionResult to a completed one.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
results: The job results to populate.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
ExecutionResult: A new ExecutionResult instance with results populated
|
|
68
|
+
and job_id preserved.
|
|
69
|
+
"""
|
|
70
|
+
return replace(self, results=results)
|