ZNE

class ZNE(scale_factors, folding_fn=None, extrapolator=None)[source]

Bases: QEMProtocol

Zero Noise Extrapolation.

For each scale factor, applies a folding function to produce a noise-scaled circuit, then extrapolates the per-scale expectation values to s=0 with the provided extrapolator.

Choosing a folding strategy

  • global_fold() (default) — applies (U†·U)^k + partial tail fold at the circuit level. Deterministic, good first choice for widely-spaced scales (e.g. [1, 3, 5]).

  • local_fold() — per-gate folding with fractional-scale support. Use when you need scales close to 1 ([1.0, 1.25, 1.5]) on deep circuits where global folding would explode the gate count, or when you want to skip specific gates during folding (exclude={"cx"}). global_fold has no equivalent exclude mechanism — it folds the whole unitary.

Effective vs requested scales

The achievable scale factors form a discrete grid of granularity 2/d (d = foldable gate count). For small d a requested non-integer scale may snap to a different value. expand reports the effective scale factors via the context and reduce() forwards them to the extrapolator, so extrapolation stays unbiased. A warning is emitted if two requested scales collapse to the same effective value.

Parameters:
  • scale_factors (Sequence[float]) – Noise scale factors (≥ 1; e.g. [1, 3, 5] or [1.0, 1.5, 2.0]). Arbitrary real values ≥ 1 are supported by both default folds.

  • folding_fn (Callable[[DAGCircuit, float], tuple[DAGCircuit, float]] | None) – (DAGCircuit, scale) (DAGCircuit, effective_scale). Defaults to global_fold(). Pass local_fold() (or functools.partial(local_fold, selection=...)) for local folding, or any custom callable. See FoldingFn for the input-mutation contract and the scale=1.0 pass-through convention.

  • extrapolator (ZNEExtrapolator | None) – Any object with an extrapolate(scale_factors, results) -> float method. No subclassing required — just implement the method. Defaults to RichardsonExtrapolator.

Example — switch to local folding:

from divi.circuits.qem import ZNE, local_fold

zne = ZNE(
    scale_factors=[1.0, 1.5, 2.0, 2.5, 3.0],
    folding_fn=local_fold,
)

Example — custom folding + custom extrapolator:

def my_fold(dag, scale):
    ...  # your folding logic
    return folded_dag, effective_scale  # both required

class MyExtrapolator:
    def extrapolate(self, scale_factors, results):
        ...
        return zero_noise_value

zne = ZNE(
    scale_factors=[1.0, 1.5, 2.0],
    folding_fn=my_fold,
    extrapolator=MyExtrapolator(),
)

Attributes Summary

Methods Summary

dry_expand(dag[, observable])

One unfolded alias of dag per scale factor; never mutates the input (dry batches may alias one DAG across entries).

expand(dag[, observable])

Generate DAGs and classical context for error mitigation.

reduce(quantum_results, context)

Extrapolate per-observable expectation values to s=0.

Attributes Documentation

extrapolator
folding_fn
name
scale_factors

Methods Documentation

dry_expand(dag, observable=None)[source]

One unfolded alias of dag per scale factor; never mutates the input (dry batches may alias one DAG across entries).

Return type:

tuple[tuple[DAGCircuit, ...], dict]

expand(dag, observable=None)[source]

Generate DAGs and classical context for error mitigation.

The input dag is consumed by this method: implementations may mutate it, and callers must not retain it expecting the original state. This matches the broader pipeline convention for consumes_dag_bodies stages (see BundleStage).

Parameters:
  • dag (DAGCircuit) – Circuit to mitigate.

  • observable (tuple[SparsePauliOp, ...] | None) – tuple[SparsePauliOp, ...] (one entry per expectation value being measured), or None.

Return type:

tuple[tuple[DAGCircuit, ...], dict]

reduce(quantum_results, context)[source]

Extrapolate per-observable expectation values to s=0.

Each entry of quantum_results is a list[float] of per- observable expectation values from one scale factor. Extrapolation runs independently per observable.

Return type:

list[float]