@@ -87,6 +87,23 @@ struct extended_dem {
8787
8888 // / Number of outgoing-seam rows, i.e. rows of out_syndrome.
8989 uint32_t num_out_seam_rows () const ;
90+
91+ // / @brief Throw std::invalid_argument unless this chunk is internally
92+ // / consistent, i.e. safe to stitch, close, or merge.
93+ // /
94+ // / num_faults() reports in_syndrome's width alone, so nothing else about a
95+ // / chunk is self-describing: a block that disagrees with it, or a prior list
96+ // / that does not have one entry per fault, would otherwise be read at the
97+ // / wrong width and silently misalign columns (or walk past the end of a
98+ // / nested column list). Checks that
99+ // / - every block is num_faults() columns wide, including zero-row blocks
100+ // / (use a width-n empty matrix, not a default-constructed one, when the
101+ // / chunk has faults),
102+ // / - fault_priors has one entry per fault, and
103+ // / - each tag vector has one entry per row of the seam it names.
104+ // /
105+ // / @param context Prefix for the error message, naming the caller.
106+ void validate (const char *context) const ;
90107};
91108
92109// / @brief Build a one-round extended_dem from CSS matrices and noise.
@@ -141,9 +158,10 @@ struct dem_chunk_spec {
141158 // / True when nothing has been set, used to detect an omitted phase.
142159 bool is_empty () const ;
143160
144- // / @brief Check internal consistency: a positive fault count, one error rate
145- // / per fault with each in [0, 1], and index lists that are -1 terminated
146- // / with every index in [0, num_faults).
161+ // / @brief Check internal consistency: a positive fault count that fits in
162+ // / uint32_t (sparse matrix column index width), one error rate per fault with
163+ // / each in [0, 1], and index lists that are -1 terminated with every index
164+ // / in [0, num_faults).
147165 // / @param context Prefix for error messages, e.g. "dem_chunks.init".
148166 // / @throws std::invalid_argument on the first violation.
149167 void validate (const std::string &context) const ;
@@ -260,12 +278,18 @@ extended_dem dem_stitch_all(const std::vector<extended_dem> &dem_chunks);
260278// / into one. The merged prior is computed from the individual priors using
261279// / one of these two rules:
262280// /
263- // / - or_combine (default): p_merged = 1 - prod_i(1 - p_i)
264- // / Exact probability that at least one independent event fires.
265- // / Use for physical fault mechanisms.
266- // /
267- // / - sum_combine: p_merged = sum_i(p_i)
268- // / Linear approximation valid when all p_i are small.
281+ // / - or_combine (default): p_merged = 1/2 * (1 - prod_i(1 - 2 p_i))
282+ // / Exact probability that an odd number of independent events fire, which
283+ // / is the net GF(2) effect of identical DEM columns (even counts cancel).
284+ // / Pairwise this is P(A xor B) = p + q - 2 p q, matching
285+ // / detector_error_model canonicalization. Prefer this for physical fault
286+ // / mechanisms.
287+ // /
288+ // / - sum_combine: p_merged = min(1, sum_i(p_i))
289+ // / Linear approximation valid when all p_i are small. The sum of several
290+ // / larger priors can exceed 1, which is not a probability any decoder can
291+ // / use, so the result is clamped; prefer or_combine when the priors are
292+ // / not small.
269293enum class prior_combine_mode { or_combine, sum_combine };
270294
271295// / @brief Merge fault columns with identical row support into single columns.
@@ -424,7 +448,10 @@ dem_chunks_to_o_sparse(const std::vector<extended_dem> &dem_chunks);
424448// /
425449// / Seams only have to contract pairwise, so a phase decomposition works: the
426450// / first chunk may have no incoming seam (its interior carries round 0) and the
427- // / last may have no outgoing one.
451+ // / last may have no outgoing one. As with dem_close(), the last chunk's
452+ // / out_syndrome is discarded: any detector that should appear in the closed
453+ // / DEM must already live in some chunk's in_syndrome or interior (for a
454+ // / dem_chunks_spec final phase, that means H_in_sparse / H_mid_sparse).
428455// /
429456// / @param dem_chunks Non-empty sequence of chunks in round order. Each chunk's
430457// / out_syndrome must match the next one's in_syndrome, and all
@@ -457,7 +484,15 @@ dem_chunks_to_pcm(const std::vector<extended_dem> &dem_chunks);
457484// / - detector_error_matrix: [in_syndrome stacked above interior]
458485// / - observables_flips_matrix: observables
459486// / - error_rates: fault_priors
460- // / - out_syndrome rows are dropped (no final data measurement assumed).
487+ // /
488+ // / out_syndrome is intentionally dropped. Closing models a terminated
489+ // / experiment: there is no later round for the outgoing seam to differ against,
490+ // / matching dem_from_css_matrices (final-round faults touch only the last
491+ // / detector band). Put any detector that must survive closing into in_syndrome
492+ // / or interior instead — for example a final data-readout boundary belongs in
493+ // / the last chunk's in_syndrome / interior (dem_chunks.final.H_in_sparse /
494+ // / H_mid_sparse), never only in out_syndrome. dem_chunks_spec::validate()
495+ // / already rejects a nonempty final.H_out_sparse for this reason.
461496// /
462497// / Invariant (up to canonicalization):
463498// / dem_close(dem_stitch_all(T one-round chunks))
0 commit comments