5757
5858from cudaq_qec import NMOptimizer , make_training_step
5959
60- EXECUTE_MODES = ("codegen" , "unrolled" , "opt_einsum" )
60+ EXECUTE_MODES = ("codegen" , "unrolled" , "opt_einsum" , "cutensornet" )
6161
6262
6363@dataclass
@@ -198,6 +198,22 @@ def path_opt_cost(info: Any) -> float:
198198 return float ("nan" )
199199
200200
201+ def einsum_rank (eq : str ) -> int :
202+ if "->" not in eq :
203+ return 0
204+ lhs , rhs = eq .split ("->" , 1 )
205+ terms = [term for term in lhs .split ("," ) if term ]
206+ terms .append (rhs )
207+ return max ((len (term ) for term in terms ), default = 0 )
208+
209+
210+ def path_max_einsum_rank (info : Any ) -> int :
211+ ranks = [
212+ einsum_rank (step [2 ]) for step in getattr (info , "contraction_list" , ())
213+ ]
214+ return max (ranks , default = 0 )
215+
216+
201217def fmt_float (value : float ) -> str :
202218 if math .isnan (value ):
203219 return "n/a"
@@ -252,25 +268,32 @@ def construct_optimizer(problem: Problem, syn: np.ndarray, flips: np.ndarray,
252268 compile = args .compile )
253269
254270
255- def run_case (problem : Problem , batch_size : int , execute : str ,
256- args : argparse .Namespace ) -> dict [str , str ]:
271+ def run_case (problem : Problem ,
272+ batch_size : int ,
273+ execute : str ,
274+ args : argparse .Namespace ,
275+ fixed_path : Any | None = None ,
276+ fixed_path_source : str = "" ) -> tuple [dict [str , str ], Any | None ]:
257277 label = f"batch={ batch_size } , execute={ execute } "
258278 row = {
259279 "batch" : str (batch_size ),
260280 "construct_batch" : str (batch_size ),
261281 "execute" : execute ,
282+ "path_source" : fixed_path_source if fixed_path is not None else "self" ,
262283 "status" : "FAIL" ,
263284 "construct_s" : "n/a" ,
264285 "largest_intermediate" : "n/a" ,
265286 "intermediate_gib" : "n/a" ,
266287 "opt_cost" : "n/a" ,
288+ "max_einsum_rank" : "n/a" ,
267289 "step_s" : "n/a" ,
268290 "peak_mem_mib" : "n/a" ,
269291 "error" : "" ,
270292 }
271293 init_priors = make_initial_priors (problem .priors , args .init )
272294 syn , flips = sample_problem (problem , batch_size , args .seed + batch_size )
273295
296+ selected_path = None
274297 try :
275298 clear_cuda (args .device )
276299 with timed (f"Construct NMOptimizer ({ label } )" ):
@@ -310,6 +333,10 @@ def _progress(done: int, total: int, elapsed: float) -> None:
310333 opt = construct_optimizer (problem , syn , flips , init_priors ,
311334 args , execute )
312335 step_fn = None
336+ if fixed_path is not None :
337+ print (f"Reusing reduced path from { fixed_path_source } " ,
338+ flush = True )
339+ opt .optimize_path (fixed_path )
313340 row ["construct_s" ] = f"{ time .perf_counter () - t0 :.3f} "
314341
315342 construct_batch = int (getattr (opt , "_batch_size" , batch_size ))
@@ -319,10 +346,12 @@ def _progress(done: int, total: int, elapsed: float) -> None:
319346 run_label += f", construct_batch={ construct_batch } "
320347
321348 info = getattr (opt , "_reduced_info" , None )
349+ selected_path = getattr (opt , "path_batch" , None )
322350 largest = path_largest_intermediate (info )
323351 cost = path_opt_cost (info )
324352 row ["largest_intermediate" ] = fmt_float (largest )
325353 row ["opt_cost" ] = fmt_float (cost )
354+ row ["max_einsum_rank" ] = str (path_max_einsum_rank (info ))
326355 if not math .isnan (largest ):
327356 gib = largest * element_size (args .dtype ) / 2 ** 30
328357 row ["intermediate_gib" ] = f"{ gib :.3f} "
@@ -343,15 +372,15 @@ def _progress(done: int, total: int, elapsed: float) -> None:
343372
344373 row ["peak_mem_mib" ] = peak_mem_mib (args .device )
345374 row ["status" ] = "OK"
346- return row
375+ return row , selected_path
347376 except Exception as exc : # intentionally broad: this is an OOM probe.
348377 row ["error" ] = repr (exc )
349378 with contextlib .suppress (Exception ):
350379 row ["peak_mem_mib" ] = peak_mem_mib (args .device )
351380 print (f"FAILED CASE { label } : { exc !r} " , flush = True )
352381 if args .stop_on_failure :
353382 raise
354- return row
383+ return row , selected_path
355384 finally :
356385 with contextlib .suppress (Exception ):
357386 del opt # type: ignore[name-defined]
@@ -396,7 +425,7 @@ def make_parser() -> argparse.ArgumentParser:
396425 parser .add_argument (
397426 "--execute" ,
398427 default = "all" ,
399- help = "all, codegen, unrolled, opt_einsum, or comma list" )
428+ help = "all, codegen, unrolled, opt_einsum, cutensornet, or comma list" )
400429 parser .add_argument ("--device" , default = "cuda:0" )
401430 parser .add_argument ("--dtype" ,
402431 choices = ["float32" , "float64" ],
@@ -423,6 +452,11 @@ def make_parser() -> argparse.ArgumentParser:
423452 type = int ,
424453 default = 10 ,
425454 help = "Print batch-slice progress every N chunks." )
455+ parser .add_argument (
456+ "--reuse-first-path" ,
457+ action = "store_true" ,
458+ help = ("For each requested batch size, reuse the first execute mode's "
459+ "reduced contraction path for later execute modes." ))
426460 parser .add_argument ("--lr" ,
427461 type = float ,
428462 default = 1e-2 ,
@@ -456,6 +490,7 @@ def main() -> int:
456490 f"d={ args .distance } , r={ args .rounds } , device={ args .device } , "
457491 f"dtype={ args .dtype } , execute={ execute_modes } , "
458492 f"batch_sizes={ args .batch_sizes } , run_step={ args .run_step } , "
493+ f"reuse_first_path={ args .reuse_first_path } , "
459494 f"batch_slice_size={ args .batch_slice_size } " ,
460495 flush = True )
461496
@@ -477,14 +512,26 @@ def main() -> int:
477512
478513 rows = []
479514 for batch_size in args .batch_sizes :
515+ fixed_path = None
516+ fixed_path_source = ""
480517 for execute in execute_modes :
481- rows .append (run_case (problem , batch_size , execute , args ))
518+ row , selected_path = run_case (problem ,
519+ batch_size ,
520+ execute ,
521+ args ,
522+ fixed_path = fixed_path ,
523+ fixed_path_source = fixed_path_source )
524+ rows .append (row )
525+ if (args .reuse_first_path and fixed_path is None and
526+ row ["status" ] == "OK" and selected_path is not None ):
527+ fixed_path = selected_path
528+ fixed_path_source = f"execute={ execute } "
482529
483530 print ("\n ## NMOptimizer d=5/r=5 Large-DEM Memory Matrix" )
484531 headers = [
485- "batch" , "construct_batch" , "execute" , "status " , "construct_s " ,
486- "largest_intermediate " , "intermediate_gib " , "opt_cost " , "step_s " ,
487- "peak_mem_mib" , "error"
532+ "batch" , "construct_batch" , "execute" , "path_source " , "status " ,
533+ "construct_s " , "largest_intermediate " , "intermediate_gib " , "opt_cost " ,
534+ "max_einsum_rank" , "step_s" , " peak_mem_mib" , "error"
488535 ]
489536 print ("| " + " | " .join (headers ) + " |" )
490537 print ("| " + " | " .join (["---" ] * len (headers )) + " |" )
0 commit comments