MerlinProcessor API Reference
Overview
MerlinProcessor is an RPC-style bridge that offloads quantum leaves
(e.g., layers exposing export_config()) to a Perceval
RemoteProcessor, while keeping classical layers local.
It supports batched execution with chunking, limited intra-leaf concurrency,
per-call/global timeouts, cooperative cancellation, and a Torch-friendly async
interface returning torch.futures.Future.
Key Capabilities
Automatic traversal of a PyTorch module; offloads only quantum leaves.
Batch chunking (
microbatch_size) and parallel submission per leaf (chunk_concurrency).Synchronous (
forward) and asynchronous (forward_async) APIs.Cancellation of a single call or all calls in flight.
Timeouts that cancel in-flight cloud jobs.
Per-call pool of cloned
RemoteProcessorobjects (avoid cross-thread handler sharing).Stable, descriptive cloud job names (capped to 50 chars).
Note
Execution is supported both with exact probabilities (if the backend exposes
the "probs" command) and with sampling ("sample_count" or
"samples"). Shots are user-controlled via nsample; there is no
hidden auto-shot selection.
Class Reference
MerlinProcessor
- class merlin.core.merlin_processor.MerlinProcessor(remote_processor, microbatch_size=32, timeout=3600.0, max_shots_per_call=None, chunk_concurrency=1)
Create a processor that offloads quantum leaves to the given Perceval
RemoteProcessor.- Parameters:
remote_processor (perceval.runtime.RemoteProcessor) – Authenticated Perceval remote processor (simulator or QPU-backed).
microbatch_size (int) – Maximum rows per cloud job (chunk size). Values > 32 are accepted but effectively capped by platform/job strategy.
timeout (float) – Default wall-time limit (seconds) per call. Must be a finite float. Per-call override via
timeout=...on API methods.max_shots_per_call (int | None) – Hard cap on shots per cloud call. If
None, a safe default is used internally.chunk_concurrency (int) – Max number of chunk jobs in flight per quantum leaf during a single call.
>=1(default: 1, i.e., serial).
Attributes
- backend_name
str- Best-effort backend name ofremote_processor.
- available_commands
list[str]- Commands exposed by the backend (e.g.,"probs","sample_count","samples"). Empty if unknown.
Context Management
- merlin.core.merlin_processor.__enter__()
- merlin.core.merlin_processor.__exit__(exc_type, exc, tb)
Entering returns the processor. Exiting triggers a best-effort
cancel_all()to ensure no stray jobs remain.
Execution APIs
- merlin.core.merlin_processor.forward(module, input, *, nsample=None, timeout=None) torch.Tensor
Synchronous convenience around
forward_async().- Parameters:
module (torch.nn.Module) – A Torch module/tree. Leaves exposing
export_config()(and notforce_local=True) are offloaded.input (torch.Tensor) – 2D batch
[B, D]or shape required by the first leaf. Tensors are moved to CPU for remote execution if needed; the result is moved back to the input’s original device/dtype.nsample (int | None) – Shots per input when sampling. Ignored if the backend supports exact probabilities (
"probs").timeout (float | None) – Per-call override.
None/0== unlimited.
- Returns:
Output tensor with batch dimension
Band leaf-determined distribution dimension.- Return type:
- Raises:
RuntimeError – If
moduleis in training mode.TimeoutError – On global per-call timeout (remote cancel is issued).
concurrent.futures.CancelledError – If the call is cooperatively cancelled via the async API.
- merlin.core.merlin_processor.forward_async(module, input, *, nsample=None, timeout=None) torch.futures.Future
Asynchronous execution. Returns a
torch.futures.Futurewith extra helpers attached:Future extensions
future.job_ids: list[str]- Accumulates job IDs across all chunk jobs.future.status() -> dict- Current state/progress/message plus chunk counters:{"chunks_total", "chunks_done", "active_chunks"}.future.cancel_remote() -> None- Cooperative cancel; in-flight jobs are best-effort cancelled andfuture.wait()raisesCancelledError.
Job & Lifecycle Utilities
- merlin.core.merlin_processor.cancel_all() None
Best-effort cancellation of all active jobs across outstanding calls.
- merlin.core.merlin_processor.get_job_history() list[perceval.runtime.RemoteJob]
Returns a list of all jobs observed/submitted by this instance during the process lifetime (useful for diagnostics).
- merlin.core.merlin_processor.clear_job_history() None
Clears the internal job history list.
Shot Estimation (No Submission)
- merlin.core.merlin_processor.estimate_required_shots_per_input(layer, input, desired_samples_per_input) list[int]
Ask the platform estimator how many shots are required per input row to reach a target number of useful samples.
- Parameters:
layer (torch.nn.Module) – A quantum leaf (must implement
export_config()).input (torch.Tensor) –
[B, D]or a single vector[D]. Values are mapped to the circuit parameters as they would be during execution.desired_samples_per_input (int) – Target useful samples per input.
- Returns:
list[int]of lengthB(0indicates “not viable” under current settings).- Return type:
list[int]
- Raises:
TypeError – If
layerdoes not exposeexport_config().ValueError – If
inputis not 1D or 2D.
Execution Semantics
Traversal & Offload
Leaves with
export_config()are treated as quantum leaves and are offloaded unless they expose ashould_offload(remote_processor, nsample)method that returnsFalse, or they setforce_local=True.Non-quantum leaves run locally under
torch.no_grad().
Batching & Chunking
If
B > microbatch_size, the batch is split into chunks of size<= microbatch_size. Up tochunk_concurrencychunk jobs per quantum leaf are submitted in parallel.
Backends & Commands
If the backend exposes
"probs", the processor queries exact probabilities and ignoresnsample.Otherwise it uses
"sample_count"or"samples"withnsample or DEFAULT_SHOTS_PER_CALL.
Timeouts & Cancellation
Per-call timeouts are enforced as global deadlines. On expiry, in-flight jobs are cancelled and a
TimeoutErroris raised.future.cancel_remote()performs cooperative cancellation; awaiting the future raisesconcurrent.futures.CancelledError.
Job Naming & Traceability
Each chunk job receives a descriptive name of the form
"mer:{layer}:{call_id}:{idx}/{total}:{pool_slot}:{cmd}", sanitized and truncated to 50 characters with a stable hash suffix when necessary.
Threading & Pools
For each call and for each quantum leaf, the processor creates a pool of cloned
RemoteProcessorobjects sized tochunk_concurrency. Each clone has its own RPC handler to avoid cross-thread sharing.
Return Shapes & Mapping
Distribution size is inferred from the leaf graph or from
(n_modes, n_photons)and whetherno_bunchingis enabled. Probability vectors are normalized if needed.
Examples
Synchronous execution
y = proc.forward(model, X, nsample=20_000)
Asynchronous with status and cancellation
fut = proc.forward_async(model, X, nsample=5_000, timeout=None)
print(fut.status()) # {'state': ..., 'progress': ..., ...}
# If needed:
fut.cancel_remote() # cooperative cancel
try:
y = fut.wait()
except Exception as e:
print("Cancelled:", type(e).__name__)
High-throughput chunking
proc = MerlinProcessor(rp, microbatch_size=8, chunk_concurrency=2)
y = proc.forward(q_layer, X, nsample=3_000)
Version Notes
Default
chunk_concurrencyis 1 (serial).The constructor
timeoutmust be a float; use per-calltimeout=Nonefor an unlimited call.Shots are user-controlled (no auto-shot chooser); use the estimator helper to plan values ahead of time.