@@ -305,6 +305,74 @@ def _localize_counts(counts, num_qubits):
305305
306306 return local
307307
308+ def _run_parallel_batch_with_retry (batch , num_shots ):
309+ """
310+ Try to execute a batch in parallel. If the partitioner rejects the full
311+ batch, recursively split it into smaller batches.
312+
313+ This prevents one bad batch from forcing the entire benchmark to fall back
314+ to sequential execution.
315+ """
316+ if len (batch ) == 1 :
317+ return _run_qiskit_parallel_experiment (batch , num_shots )
318+
319+ try :
320+ return _run_qiskit_parallel_experiment (batch , num_shots )
321+
322+ except Exception as err :
323+ print (f"... batch of { len (batch )} circuits failed: { err } " )
324+ print ("... splitting batch into smaller parallel batches" )
325+
326+ mid = len (batch ) // 2
327+
328+ left_counts = _run_parallel_batch_with_retry (batch [:mid ], num_shots )
329+ right_counts = _run_parallel_batch_with_retry (batch [mid :], num_shots )
330+
331+ return left_counts + right_counts
332+
333+ def _run_parallel_batch_with_retry (batch , num_shots ):
334+ import execute as ex
335+
336+ if len (batch ) == 1 :
337+ print (
338+ "... single-circuit batch:" ,
339+ [c .num_qubits for c in batch ],
340+ "running sequentially" ,
341+ )
342+
343+ ex .parallel_execution = False
344+ try :
345+ _ , result = ex .execute_circuits (batch , num_shots )
346+ finally :
347+ ex .parallel_execution = True
348+
349+ counts = result .get_counts ()
350+
351+ if isinstance (counts , dict ):
352+ return [counts ]
353+
354+ return counts
355+
356+ try :
357+ print (
358+ "... running parallel batch:" ,
359+ [c .num_qubits for c in batch ],
360+ "total_qubits =" ,
361+ sum (c .num_qubits for c in batch ),
362+ )
363+
364+ return _run_qiskit_parallel_experiment (batch , num_shots )
365+
366+ except Exception as err :
367+ print (f"... batch failed: { err } " )
368+ print ("... splitting batch" )
369+
370+ mid = len (batch ) // 2
371+ left_counts = _run_parallel_batch_with_retry (batch [:mid ], num_shots )
372+ right_counts = _run_parallel_batch_with_retry (batch [mid :], num_shots )
373+
374+ return left_counts + right_counts
375+
308376def execute_circuits_parallel (circuits , num_shots ):
309377 """
310378 Execute a list of QED-C circuits using the integrated Qiskit
@@ -351,18 +419,54 @@ def execute_circuits_parallel(circuits, num_shots):
351419 print (f">>> execute_circuits_parallel [qiskit]: { len (circuits )} circuits, { num_shots } shots" )
352420
353421 try :
354- # Run the circuits through the custom multiprogramming +
355- # Qiskit ParallelExperiment pipeline.
356- print ("Uses the integrated Qiskit ParallelExperiment workflow. If the parallel path fails, execution automatically falls back to the standard QED-C execution path." )
357- counts_list = _run_qiskit_parallel_experiment (circuits , num_shots )
358-
359- # ParallelExperiment may return each child result with extra classical
360- # bits from the full combined circuit. Convert each result back to the
361- # local bitstring width expected by QED-C.
362- counts_list = [
363- _localize_counts (counts_list [i ], circuits [i ].num_qubits )
364- for i in range (len (counts_list ))
365- ]
422+ # Maximum total logical qubits allowed in one ParallelExperiment batch.
423+ # This prevents trying to place too many circuits on the hardware at once.
424+ MAX_PARALLEL_QUBITS = 15
425+
426+ all_counts = []
427+
428+ # Current batch of circuits to run together.
429+ batch = []
430+ batch_qubits = 0
431+
432+ for circuit in circuits :
433+ circuit_qubits = circuit .num_qubits
434+
435+ # If adding this circuit would exceed the batch capacity, run the current
436+ # batch first, then start a new batch.
437+ if batch and batch_qubits + circuit_qubits > MAX_PARALLEL_QUBITS :
438+ batch_counts = _run_parallel_batch_with_retry (batch , num_shots )
439+
440+ # Convert each batch result back to QED-C's expected local bit width.
441+ batch_counts = [
442+ _localize_counts (batch_counts [i ], batch [i ].num_qubits )
443+ for i in range (len (batch_counts ))
444+ ]
445+
446+ # Preserve original circuit order by appending this batch's results.
447+ all_counts .extend (batch_counts )
448+
449+ # Start a fresh batch.
450+ batch = []
451+ batch_qubits = 0
452+
453+ # Add the current circuit to the active batch.
454+ batch .append (circuit )
455+ batch_qubits += circuit_qubits
456+
457+ # Run the final partially filled batch.
458+ if batch :
459+ batch_counts = _run_parallel_batch_with_retry (batch , num_shots )
460+
461+ batch_counts = [
462+ _localize_counts (batch_counts [i ], batch [i ].num_qubits )
463+ for i in range (len (batch_counts ))
464+ ]
465+
466+ all_counts .extend (batch_counts )
467+
468+ # This list now has one counts dictionary per original input circuit.
469+ counts_list = all_counts
366470
367471 # Convert the raw counts list into QED-C's standard result object so the
368472 # rest of the benchmark framework does not need to know that the circuits
0 commit comments