Skip to content

Builds of the same code calibrate to two different weight sets #598

Open

Description

@MaxGhenis

What's wrong

Pull request builds of the same data code calibrate to two distinct sets of weights. The targets are identical between them, and the difference is about five times the run-to-run dropout noise.

Evidence

Five pull request builds ran on one data code state: main from b34a4ea to 2b9c1c2 changed only CI and docs. They were docs-only, changelog-only and CI-only PRs, all TESTING builds at 32 epochs, on the same runner image (ubuntu24/20261004.327). The national rows of each run's final-epoch constituency_calibration_log.csv (epoch 30), compared pairwise as per-target change in |estimate / target − 1|:

Pair Per-target change, p99 Max Max on obr/*, ons/uk_population, dwp/* (159 targets)
Within group A: runs 38006434333, 37960852139 0.025 0.040 0.027
Within group B: runs 37959581668, 38009046475, 37864653693 0.031–0.039 0.077–0.179 0.013–0.020
Between A and B 0.163–0.170 0.607–0.620 0.210–0.215
  • The national share of targets within 10% stays within 0.646–0.655 in every build.
  • Per target, the two groups differ most on CGT bands, the benefit cap totals and the UC payment distribution.
  • Every constituency target value is identical across the five builds. Every national target value is identical too, except the Scottish council tax bands that Scottish council tax targets change with whether a download succeeds #596 explains. That difference doesn't follow the groups: run 38009046475 has group A's Scottish targets and group B's fit.
  • Neither runner region nor image follows the groups: region is centralus and eastus in both.

So something in the data build itself, before calibration, takes one of two states. The dropout noise is the much smaller within-group spread, which #507 seeds.

Why it matters

The W3 no-worsening gate compares a pull request's build with its base's, under a matched protocol. A same-code difference this large in either direction swamps the changes it is meant to attribute: 0.21 on key OBR and DWP targets. It has to be found and removed, or measured and allowed for, before a per-target comparison can gate.

Next step

Diff the pre-calibration datasets of two builds from different groups, as aggregates by variable. The divergent step probably shows up as an imputed variable whose totals take two values. Candidates are any imputation or download with unseeded randomness or order dependence.

Activity

  1. MaxGhenis commented on Oct 16, 2026

    @MaxGhenis
    ContributorAuthor

    The same nondeterminism, measured on model outputs. #602's CI runs policyengine-uk's aggregate-impact tool on both builds of a pull request, with the model held fixed (policyengine-uk 2.122.2, identical runtimes), and compares national aggregates.

    #602 changes only CI, so both builds come from the same data code. Even so, in run 38802651315 the head build differs from the base build by:

    • capital gains tax: +£0.90bn (2025-26) to +£1.14bn (2031-32) a year, about 1.7%;
    • child poverty after housing costs: +0.76 to +1.04pp, relative and absolute;
    • child poverty before housing costs: +0.58 to +0.69pp;
    • 337 moves above the report's flag thresholds in all.

    The fit comparison in the same run moved 12 of 644 national targets to a worse error band, at the top of the 2–12 range of the five same-code builds above. The CGT and UC lines match this issue's largest per-target differences (CGT bands, the UC payment distribution).

    So until this is fixed, a data PR's aggregate moves of this size can't be attributed to its change. #602 flags them but fails only on broken checks for that reason. These are national aggregates from the PR comment, which is already public; no record-level value is involved.

    🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions