Skip to content

Reshape the Guppy DEM construction API into the house builder pattern #428

Description

@ciaranra

Problem

build_dem_from_guppy(...) is a verb-named function that takes every option as
a keyword argument and returns a GuppyDemBuild noun you then unpack:

dem_build = build_dem_from_guppy(
    rep_code_memory,
    num_qubits=7,
    detectors=detectors,
    observables=observables,
    idle_after_2q_duration=1.0,
    p_idle_linear=0.01,
    p_idle_linear_model={"X": 0.25, "Y": 0.25, "Z": 0.5},
    p_idle_sin_squared=0.03,
    p_idle_sin_squared_model={"Z": 1.0},
    p1=0.002,
    p2=0.02,
    p_meas=0.02,
    p_prep=0.02,
)
dem = dem_build.dem

This reads as an inverted builder: the configuration is a flat keyword pile
(now ~30 parameters wide after the structured idle families), the call site
cannot be composed incrementally, and the result object is the thing you would
normally get from .build(). It also diverges from the house builder-of-builders
style used by sim(...)/sim_neo(...), where configuration is chained and a
terminal call produces the artifact.

Proposal (sketch, to be settled in review)

dem_build = (
    dem_from_guppy(rep_code_memory)
    .qubits(7)
    .detectors(detectors)
    .observables(observables)
    .idle_after_2q(1.0)
    .noise(gate_noise(p1=0.002, p2=0.02, p_meas=0.02, p_prep=0.02))
    .idle_noise(linear(0.01, {"X": 0.25, "Y": 0.25, "Z": 0.5}), sin_squared(0.03, {"Z": 1.0}))
    .build()
)
dem = dem_build.dem

Points to settle:

Related

Same consistency theme as #422-#425. Non-blocking for PR #420, which uses the
current shape; the docs there name the result dem_build rather than build so
the verb/noun collision at least does not appear in the examples.


Unify the two Guppy DEM entry points (added after review of the current surfaces)

There are two entry points that do the same job:

DetectorErrorModel.from_guppy build_dem_from_guppy
detectors/observables JSON strings typed Detector/Observable (bare tag strings, rec[-k], result_ref)
returns the DEM GuppyDemBuild: .dem, .audit, .schema_fingerprint, .evaluate_result_columns()
pipeline trace, normalize, idle passes, attach metadata, Rust builder identical

Measured overlap: 38 and 37 parameters, 35 shared. The only differences are
detectors_json/observables_json/num_measurements versus
detectors/observables. They produce byte-identical DEMs for the same
specification (verified while writing docs/workflows/guppy-dem-decoding.md).

The duplication is not free: every parameter added must be added twice, in two
signatures, two docstrings, and two guard wirings. PR #420 did exactly that four
times (idle passes, the three idle-noise families, the symmetric-default and
L-key round, and the p_idle removal).

Direction

One construction pipeline, two convenience shapes at the surface:

  • The builder from this issue is the single pipeline and returns the rich
    object (.dem, .audit, .evaluate_result_columns()).
  • DetectorErrorModel.from_guppy stays as the one-call convenience and keeps
    returning the DEM (changing its return type would be breaking), but delegates
    to the builder and accepts either spelling for detectors/observables:
    typed specs or the JSON strings that other tooling emits (e.g.
    meta_tc.get_meta("detectors") from the surface builder).
  • build_dem_from_guppy becomes a deprecated alias for the builder path,
    retired on the ramp described above.

Net effect: parameters live in exactly one place, the typed and JSON spellings
stop being a fork, and the audit trail plus result-column evaluator become
reachable from the documented entry point rather than only from the second one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpythonPull requests that update python code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions