1
1
import json
2
2
import time
3
+ from typing import TypeVar
3
4
from collections import OrderedDict , namedtuple
4
5
5
6
from beartype import beartype
6
7
from requests import Response , request
7
8
from beartype .typing import Any , Dict , List , Tuple , Union , Optional , NamedTuple
8
9
from pydantic .v1 .dataclasses import dataclass
9
10
11
+ from bloqade .analog .task .base import CustomRemoteTaskABC
10
12
from bloqade .analog .task .batch import RemoteBatch
11
13
from bloqade .analog .task .quera import QuEraTask
12
14
from bloqade .analog .builder .typing import LiteralType
@@ -49,7 +51,7 @@ def custom(self) -> "CustomSubmissionRoutine":
49
51
50
52
@dataclass (frozen = True , config = __pydantic_dataclass_config__ )
51
53
class CustomSubmissionRoutine (RoutineBase ):
52
- def _compile (
54
+ def _compile_single (
53
55
self ,
54
56
shots : int ,
55
57
use_experimental : bool = False ,
@@ -150,7 +152,7 @@ def submit(
150
152
)
151
153
152
154
out = []
153
- for metadata , task_ir in self ._compile (shots , use_experimental , args ):
155
+ for metadata , task_ir in self ._compile_single (shots , use_experimental , args ):
154
156
json_request_body = json_body_template .format (
155
157
task_ir = task_ir .json (exclude_none = True , exclude_unset = True )
156
158
)
@@ -161,6 +163,139 @@ def submit(
161
163
162
164
return out
163
165
166
+ RemoteTaskType = TypeVar ("RemoteTaskType" , bound = CustomRemoteTaskABC )
167
+
168
+ def _compile_custom_batch (
169
+ self ,
170
+ shots : int ,
171
+ RemoteTask : type [RemoteTaskType ],
172
+ use_experimental : bool = False ,
173
+ args : Tuple [LiteralType , ...] = (),
174
+ name : Optional [str ] = None ,
175
+ ) -> RemoteBatch :
176
+ from bloqade .analog .submission .capabilities import get_capabilities
177
+ from bloqade .analog .compiler .passes .hardware import (
178
+ assign_circuit ,
179
+ analyze_channels ,
180
+ generate_ahs_code ,
181
+ generate_quera_ir ,
182
+ validate_waveforms ,
183
+ canonicalize_circuit ,
184
+ )
185
+
186
+ if not issubclass (RemoteTask , CustomRemoteTaskABC ):
187
+ raise TypeError (f"{ RemoteTask } must be a subclass of CustomRemoteTaskABC." )
188
+
189
+ circuit , params = self .circuit , self .params
190
+ capabilities = get_capabilities (use_experimental )
191
+
192
+ tasks = OrderedDict ()
193
+
194
+ for task_number , batch_params in enumerate (params .batch_assignments (* args )):
195
+ assignments = {** batch_params , ** params .static_params }
196
+ final_circuit , metadata = assign_circuit (circuit , assignments )
197
+
198
+ level_couplings = analyze_channels (final_circuit )
199
+ final_circuit = canonicalize_circuit (final_circuit , level_couplings )
200
+
201
+ validate_waveforms (level_couplings , final_circuit )
202
+ ahs_components = generate_ahs_code (
203
+ capabilities , level_couplings , final_circuit
204
+ )
205
+
206
+ task_ir = generate_quera_ir (ahs_components , shots ).discretize (capabilities )
207
+
208
+ tasks [task_number ] = RemoteTask .from_compile_results (
209
+ task_ir ,
210
+ metadata ,
211
+ ahs_components .lattice_data .parallel_decoder ,
212
+ )
213
+
214
+ batch = RemoteBatch (source = self .source , tasks = tasks , name = name )
215
+
216
+ return batch
217
+
218
+ @beartype
219
+ def run_async (
220
+ self ,
221
+ shots : int ,
222
+ RemoteTask : type [RemoteTaskType ],
223
+ args : Tuple [LiteralType , ...] = (),
224
+ name : Optional [str ] = None ,
225
+ use_experimental : bool = False ,
226
+ shuffle : bool = False ,
227
+ ** kwargs ,
228
+ ) -> RemoteBatch :
229
+ """
230
+ Compile to a RemoteBatch, which contain
231
+ QuEra backend specific tasks,
232
+ and run_async through QuEra service.
233
+
234
+ Args:
235
+ shots (int): number of shots
236
+ args (Tuple): additional arguments
237
+ name (str): custom name of the batch
238
+ shuffle (bool): shuffle the order of jobs
239
+
240
+ Return:
241
+ RemoteBatch
242
+
243
+ """
244
+ batch = self ._compile_custom_batch (
245
+ shots , RemoteTask , use_experimental , args , name
246
+ )
247
+ batch ._submit (shuffle , ** kwargs )
248
+ return batch
249
+
250
+ @beartype
251
+ def run (
252
+ self ,
253
+ shots : int ,
254
+ RemoteTask : type [RemoteTaskType ],
255
+ args : Tuple [LiteralType , ...] = (),
256
+ name : Optional [str ] = None ,
257
+ use_experimental : bool = False ,
258
+ shuffle : bool = False ,
259
+ ** kwargs ,
260
+ ) -> RemoteBatch :
261
+ """Run the custom task and return the result.
262
+
263
+ Args:
264
+ shots (int): number of shots
265
+ RemoteTask (type): type of the remote task, must subclass of CustomRemoteTaskABC
266
+ args (Tuple): additional arguments for remaining un
267
+ name (str): name of the batch object
268
+ shuffle (bool): shuffle the order of jobs
269
+ """
270
+ if not callable (getattr (RemoteTask , "pull" , None )):
271
+ raise TypeError (
272
+ f"{ RemoteTask } must have a `pull` method for executing `run`."
273
+ )
274
+
275
+ batch = self .run_async (
276
+ shots , RemoteTask , args , name , use_experimental , shuffle , ** kwargs
277
+ )
278
+ batch .pull ()
279
+ return batch
280
+
281
+ @beartype
282
+ def __call__ (
283
+ self ,
284
+ * args : LiteralType ,
285
+ RemoteTask : type [RemoteTaskType ] | None = None ,
286
+ shots : int = 1 ,
287
+ name : Optional [str ] = None ,
288
+ use_experimental : bool = False ,
289
+ shuffle : bool = False ,
290
+ ** kwargs ,
291
+ ) -> RemoteBatch :
292
+ if RemoteTask is None :
293
+ raise ValueError ("RemoteTask must be provided for custom submission." )
294
+
295
+ return self .run (
296
+ shots , RemoteTask , args , name , use_experimental , shuffle , ** kwargs
297
+ )
298
+
164
299
165
300
@dataclass (frozen = True , config = __pydantic_dataclass_config__ )
166
301
class QuEraHardwareRoutine (RoutineBase ):
0 commit comments