Skip to content

UK local solve through the public calibrate front door, increment 2 of #612 - #634

Open
juaristi22 wants to merge 5 commits into
mainfrom
uk-frame-inc2-local-front-door
Open

UK local solve through the public calibrate front door, increment 2 of #612#634
juaristi22 wants to merge 5 commits into
mainfrom
uk-frame-inc2-local-front-door

Conversation

@juaristi22

@juaristi22 juaristi22 commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Increment 2 of #612, on the #618 Frame carrier now on main: the UK local/rowwise solve moves onto the public populace.calibrate front door — the same entry point the US production paths use (build_us_acs_local_release.py, build_us_fiscal_refresh_release.py, the exact-k ladder) — so UK weights take the kernel-enforced WeightKind.CALIBRATED transition and the kernel mints the mass record. All six roadmap commits have landed; draft pending the final CI pass and the compile-cost probe note.

Commit roadmap

  1. Delete the stacked research harness (landed). The pre-ladder areas x households cross-product solve had zero production callers (tests only), was superseded by the rowwise ladder path, and structurally can never route through calibrate() — its weight vector is longer than any entity table. Removed: solve_stacked_local_weights, the stacked doctrine route, local_runner's candidate build path, and the stacked matrix/long-weights helpers in local_geography. Kept: solve_prepared_local_weights + StackedLocalSolveResult (the rowwise path consumes them until commit 3), the reviewed doctrine constants, past_cap_census, and align_area_targets. Everything deleted was UK-specific uk_runtime/ code — nothing in country-agnostic populace-calibrate is touched. The uniform-target-surface check moved beside its only remaining consumer in local_rowwise. sort_households_by_id retires with its only consumer; positional-assignment safety on the surviving paths rides on the Frame kernel's sorted-group-id invariants and validate_uk_national_frame.
  2. Ladder-clone result becomes Frame-native, in memory (landed). Per-entity clone-index columns ({entity}_clone_index) so Frame's column-uniqueness holds; the written H5 keeps the legacy schema (single clone_index per table — the writer renames at the export boundary), asserted by an old-vs-new payload test on synthetic tables.
  3. Rowwise solve through public calibrate() (landed). Area×metric targets as a declarative TargetSet (area membership as filters, Target.metadata carrying area/metric labels), today's solver constants passed explicitly and pinned (epochs 512, lr 0.15, ratio 100.0, cap 10.0), kernel-minted mass record reconciled with the writer's chain-currency fence, past-cap census keys unchanged. The three private populace.calibrate.solve imports die here. A differential test pins the declarative compile to the COO assembly's matvec row for row; the kernel record now declares the realized mass factor (the hand-minted predecessor left it None). Compile-cost probe at real scale: recorded when the next credentialed candidate build runs.
  4. Retire the unfenced dataset: Any surface (landed) (rowwise_dataset narrowing to Frame | str | Path, the candidate tool).
  5. max_weight_ratio anchors differently per arm: 5x vs design (dense) but ~25x effective (sparse refit re-anchors) — declare one stretch contract #493 stretch-contract adjudication record (landed) (no numeric change; revision stays with the UK dense/local epic: rowwise constituency-grain dataset by cloning the national staging pool (US dense-arm analog) #495 increment-6 measured-fit review) + the honest household_weight-drop assessment (blocked by UK carrier swap, increment 1 of #612: carry the national build on populace Frames (stacked on #610 + #617) #618's own carrier contract and One country-agnostic gate battery: phased evaluation, unconditional reporting, per-country gate selection (#578) #611-fenced readers; documented, deferred).
  6. Changelog (landed, extended with each commit).

Fences

  • Touches none of the One country-agnostic gate battery: phased evaluation, unconditional reporting, per-country gate selection (#578) #611-consumer-half gate files (terminal_gates.py, weighted_integrity.py, release_input_coverage.py, national_build.py).
  • Runs in parallel with the increment-3 stage-runtime PR; file-disjoint by construction, and that PR keeps load_uk_national_frame's signature stable for the candidate tool here.
  • firm_generation.py's two private imports are out of scope (follow-up issue; its per-target loss weights and external torch matrix need their own public-seam design).

Refs #612, #578 (item 2.5 ladder), #493, #495.

🤖 Generated with Claude Code

juaristi22 and others added 4 commits August 7, 2026 15:52
The pre-ladder stacked areas x households cross-product solve had zero
production callers (tests only), was superseded by the rowwise ladder
path, and structurally can never route through the public calibrate()
front door this increment migrates onto — its weight vector is longer
than any entity table. Removed: solve_stacked_local_weights, the stacked
doctrine route, local_runner's candidate build path, and the stacked
matrix/long-weights helpers in local_geography. Everything deleted was
UK-specific uk_runtime code; country-agnostic populace-calibrate is
untouched.

Kept: solve_prepared_local_weights and StackedLocalSolveResult (the
rowwise path consumes them until the calibrate() migration lands), the
reviewed doctrine constants, past_cap_census, and align_area_targets.
The uniform-target-surface check moved beside its only remaining consumer
in local_rowwise. sort_households_by_id retires with its only consumer:
positional-assignment safety on the surviving paths rides on the Frame
kernel's sorted-group-id invariants and validate_uk_national_frame. The
SPI lineage test now asserts the anti-doubling property directly on
source_household_key instead of through the retired long-weights
representation.

Refs #612, #578.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rowwise release path's clone result was the last UK shadow carrier on
this arm: three raw DataFrames plus a weight-kind string and a mass log,
kept off Frame only by the artifact's shared clone_index column name on
all three entity tables (Frame's flattening rule refuses it). Clone
indices now land on per-entity {entity}_clone_index columns in memory, so
the result carries a validated UK national frame — typed household
weights under the #501 kind-fence chain, the mass log, and time_period as
frame metadata — and the cross-clone link-alignment fence reads the
per-entity names.

The export boundary is unchanged: the writer materializes engine tables
from the frame (typed weights authoritative) and renames the per-entity
columns back to the artifact's single clone_index name per table, in
place, preserving column order; the ladder-clone test pins the written
schema (column lists, clone_index position, attrs) so a carrier change
can never leak into the H5. The candidate tool asserts the kernel kind
transition explicitly and hard-constructs the calibrated frame with the
refreshed weight column and appended calibration record — the #618 stage
idiom — until the calibrate() front-door migration owns both. The
crosswalk route keeps its table-carrying result; its carrier is a
recorded follow-on.

The pool-lineage fixture now ascends its household ids: every production
producer is a Frame load, which guarantees sorted group ids, and the
carrier lets Frame raise on unsorted input rather than sorting rows
itself (the FRS-2024-25 bug class).

Refs #612, #495.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nt (#612 inc 2)

clone_uk_dataset_with_ladder_geography refuses the duck-typed in-memory
carrier: an in-memory input must be a populace Frame (weight kind and mass
log from the typed weights and frame, nothing defaultable), and only an
H5 path keeps the documented attribute-less DESIGN semantics. The
crosswalk route keeps its duck input — its carrier swap is the recorded
follow-on. Test ducks became real frame constructions; the negative-weight
guard is exercised at the raw-table entry, where a kernel-refused input
cannot mask it.

UK_LOCAL_MAX_WEIGHT_RATIO carries the populace#493 adjudication record:
the bound stays 100.0 (vs the US design bound 5.0, realized 4.994, and
the exact-k ladder's 20.0) because tightening without measured fit would
be an arbitrary cutoff; the revision path remains the #495 increment-6
calibrated-candidate review through this constant and its pinned test.

national_frame's docstring records the household_weight column-drop
assessment: the column is a materialized export contract regenerated by
engine_tables, the carrier's own validation makes it load-bearing, and
the drop is sequenced behind the #611 consumer half and the reader moves.

Refs #612, #493, #495.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…inc 2)

The UK local solve was the last consumer of populace.calibrate's private
internals (_optimize, _search_l0_lambda_for_budget,
_torch_constraint_matrix) — it hand-assembled a COO/CSR system because it
had no Frame at the solve site. With the ladder clone Frame-native, the
doctrine solve now expresses the area x metric surface as a declarative
TargetSet (the metric vector as the measure, area membership as the
filter, both closing over the problem's own arrays) and calls the same
public calibrate() entry the US production paths use. The kernel enforces
the CALIBRATED kind transition and mints the calibration mass record;
calibrate() gains an additive mass_reason parameter so a free-mass record
can carry domain context (the bound target families) instead of the
generic reason, and the record now declares the realized factor.

The hand-assembled UKRowwiseLocalMatrix stays as the fail-closed surface
definition and evidence substrate: builder refusals (uncovered areas,
unreachable nonzero targets), the per-area support summary, the labelled
diagnostics frame, and the populace#492 past-cap census all keep their
pre-migration vocabulary — and a differential test pins the declarative
compile to the COO assembly's matvec row for row, so the two
representations provably derive from the same numbers. The doctrine's
constants ride into the front door explicitly and are pinned by a
forwarding test (calibrate's own defaults differ, so default-drift would
change behaviour). solve_prepared_local_weights, StackedLocalSolveResult,
the optimizer floor (zero base weights are refused; tiny positives are
legal seeds), and the hand-minted rowwise_calibration_mass_record retire;
local_solver.py dissolves into local_rowwise.py.

Refs #612, #493, #495.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… precision (#612 inc 2)

The review's blocker: the production staging input's household table
carries a candidate-tier clone_index (SPI/pool lineage) that the synthetic
fixtures never had. The pre-Frame clone silently overwrote it; the Frame
carrier left it beside the new per-entity name, and the writer's rename
then produced duplicate column labels — a raw pandas crash after the full
solve. The clone now replaces the inherited column explicitly at entry
(same artifact semantics as the pre-Frame writer, now documented), and the
reserved {entity}_clone_index names fail closed instead of being silently
overwritten. Regression tests cover both, on the production input shape.

Also from the review: the candidate tool refuses _CONSERVE_MASS upfront
(the manifest's calibration_mass_change block reads the kernel's free-mass
record; flipping the constant used to brick the tool after the solve);
calibrate() records mass_reason in CalibrationResult.options so manifest
consumers see the declared reason, not only the mass log; and the doctrine
solve reports initial_loss in float64 from the compiled estimates again
(the trajectory head is a float32 optimizer value — the reported figure
had drifted at the eighth digit).

Refs #612.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant