11import json
22import time
3+ from typing import TypeVar
34from collections import OrderedDict , namedtuple
45
56from beartype import beartype
67from requests import Response , request
78from beartype .typing import Any , Dict , List , Tuple , Union , Optional , NamedTuple
89from pydantic .v1 .dataclasses import dataclass
910
11+ from bloqade .analog .task .base import CustomRemoteTaskABC
1012from bloqade .analog .task .batch import RemoteBatch
1113from bloqade .analog .task .quera import QuEraTask
1214from bloqade .analog .builder .typing import LiteralType
@@ -49,7 +51,7 @@ def custom(self) -> "CustomSubmissionRoutine":
4951
5052@dataclass (frozen = True , config = __pydantic_dataclass_config__ )
5153class CustomSubmissionRoutine (RoutineBase ):
52- def _compile (
54+ def _compile_single (
5355 self ,
5456 shots : int ,
5557 use_experimental : bool = False ,
@@ -150,7 +152,7 @@ def submit(
150152 )
151153
152154 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 ):
154156 json_request_body = json_body_template .format (
155157 task_ir = task_ir .json (exclude_none = True , exclude_unset = True )
156158 )
@@ -161,6 +163,139 @@ def submit(
161163
162164 return out
163165
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+
164299
165300@dataclass (frozen = True , config = __pydantic_dataclass_config__ )
166301class QuEraHardwareRoutine (RoutineBase ):
0 commit comments