Skip to content

Bind congressional-district interest paid as mortgage interest shares - #1231

Draft
MaxGhenis wants to merge 2 commits into
mainfrom
us-cd-interest-paid-concept
Draft

MaxGhenis wants to merge 2 commits into
mainfrom
us-cd-interest-paid-concept

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Part of #1038: the congressional-district file's interest-paid rows. Based on main, independent of #1036, #1040 and #1224. It uses the same share declarations as #1040's capital-gains rebase, but no code from it.

Methodology call, queued for Max (d1428). It binds district rows of one concept as shares of a national control rather than dropping them, the same kind of call as d539 (#1040). It changes full-surface and ACS-local state_cd target values.

Summary

22incd.csv has no Schedule A line 10 column. 22incddocguide.docx lists the line's parts:

  • N19300/A19300 "Mortgage interest paid" (line 8a);
  • N19500/A19500 home mortgage from a personal seller (8b);
  • N19530/A19530 deductible points (8c);
  • N19570/A19570 investment interest paid (line 9).

Chronicle's congressional_district_2022 package publishes only line 8a, under interest_paid_deduction_{returns,amount}. Table 2.1's rows of those ids are the line 10 total, and microcosm maps them to interest_deduction, which in PE-US 2.2.1 is deductible_mortgage_interest + non_mortgage_interest. On the pinned feed (51aa40f, 2024, aging, packaged crosswalk), on full, the district file's states and its districts each summed to:

  • $152.36B, 0.673x the national line 10 target of $226.33B;
  • 11,352,500 returns, 0.950x the national 11,944,817.

This PR binds each district-file row that reads N19300/A19300 onto deductible_mortgage_interest. Each row is its share of the file's US row, times Table 2.1's home mortgage interest control. That control is exactly 8a + 8b: $167,675,863K + $3,688,924K = $171,364,787K in TY2023.

Three remedies, measured

The sha-matched 22incd.csv (137522878a…) and Historic Table 2 TY2022 (22in55cmcsv.csv) give every number here. Scripts and outputs: ~/PolicyEngine/_reviews/cd-interest-paid-concept-20261012/. "Misallocated" is half the sum of absolute share differences, the share of the national total that sits in the wrong district.

(a) shares x line 10 control, on interest_deduction (b) drop (c) shares x home mortgage control, on deductible_mortgage_interest
What the share assumes district share of 8a = share of line 10 — district share of 8a = share of 8a + 8b
Proxy error, 436 districts (117th plan) median 7.3%, p95 17.4%, max 54.6% (FL-21) — median 0.45%, p95 1.4%, max 2.4% (CA-35)
Misallocated, districts 4.66% (about $10.5B of the aged control) — 0.21%
Proxy error, 51 states median 5.3%, max 29.8% (WY); 3.07% misallocated — median 0.38%, max 1.3% (WV); 0.17% misallocated
Against HT2's own state totals (independent publication) max 56.1% (WY); 3.39% misallocated — median 0.58%, max 3.0% (WV); 0.29% misallocated
District targets kept 974 0 974

Line 8a is 85.6% of line 10 in the file's US row. Investment interest is concentrated, so the share is 38.8% in FL-21, and 44.9% and 45.0% in NY-10 and NY-12. Line 8a is 98.2% of 8a + 8b.

(b) loses real information. A district's share of line 8a differs from its share of returns by 30.3% misallocated, from AGI by 22.2%, and from total itemized deductions by 14.0%. (c)'s proxy error is 0.21% on the same scale.

Picked (c). It keeps the geography at about a twentieth of (a)'s error. It binds the concept the model and Table 2.1 both measure, and the national control already binds that concept on the same surface. The parts it leaves without district targets, points and investment interest, are 17.7% of line 10. Table 2.1's line 10 row still binds them nationally. Return counts behave the same way: at state level the file's N19300 share is within a median 0.43% (max 2.8%, ND) of HT2's N19300 + N19500 share.

The universe filters match. Every row of either variable gets itemized_only from _SOI_ITEMIZED_ONLY_VARIABLES, so the district rows (record-set universe all_returns) and Table 2.1's rows (itemized_returns) both materialize over tax_unit_itemizes tax units. Each share row declares soi_return_universe_bridge = itemized_returns.

What changes

fiscal_targets.py:

  • _SOI_DISTRICT_COMPONENT_SHARE_COLUMNS registers N19300 and A19300 by IRS column, so a package that relabels them (for example to Table 2.1's mortgage_interest_paid_*) compiles to the same targets.
  • _soi_reference_from_fact sends them to deductible_mortgage_interest, with role soi_fiscal_distribution, and flags them.
  • A district-file interest_paid_deduction_* fact that reads any other column, or none, raises UnreviewedSoiDistrictColumnError. The file has no line 10 column, so any such fact is unreviewed.
  • _rebase_soi_district_component_shares sets control x row / US row:
    • The US row is the same record set's national all-AGI row of the same column, status and period.
    • The control is the latest national all-AGI home_mortgage_interest_* fact in the itemized universe not after the build period, from outside the district files.
    • Ties at the deciding period raise AmbiguousSoiDistrictComponentControlError or AmbiguousSoiDistrictComponentShareTotalError. Feed order never decides (Capital-gains rebase control is picked by feed order and crosses IRS concepts (HT2 N01000 vs Table 1.4 col 37) #1035).
    • The US row retires.
    • A row with no control at or after its period, or no nonzero US row, is dropped, never shipped as a level.
    • Every kept row carries soi_source_concept, soi_control_concept, soi_share_total_source_record_id, soi_source_column_id, soi_return_universe_bridge and the usual uprating_* fields. The value lands at the control's period.

tools/us_acs_local_cd_surface.py:

  • A district-file state row that names its share's denominator (is_national_share_spec) binds as compiled, as its districts' parent. It is not lifted by a sibling bridge: its states already sum to the control, so a bridge would lift amounts about 6% and counts about 2% a second time.
  • STATE_CD_LEVEL_BRIDGES drops interest_paid_deduction_*. An interest row the compiler failed to rebase would therefore refuse the surface rather than bind unbridged.
  • The receipt adds national_share_levels_by_measure and the parent basis cd_file_state_total_national_share.

Docs: docs/us-soi-interest-paid-concepts.md (new), docs/us-acs-local-soi-target-surface.md, docs/us-fact-to-target.md.

Invariants

These hold for every fact set the compile accepts:

  1. No district-file row binds interest_deduction.
  2. One factor (control / the file's US row) scales every row of one record set, column and status. Districts keep their within-state shares, and states keep their shares of the US row.
  3. Where the file's states sum to its US row, as on the pinned feed, its states sum to the control, and so do its districts.
  4. The file's US row never binds.
  5. No row without a control at or after its period, or without a nonzero US row, binds. No component level ships.
  6. No row carries the rebase flag after the compile, and rows are nonnegative.
  7. Output is independent of feed order. A common scale of the file's values (its stamp, its units) changes no target value.
  8. On state_cd, every district block sums to its parent, and a national-share state row binds at its compiled value.

test_us_soi_district_interest_shares.py covers these:

  • example tests: shares and declarations, relabeled measure ids, refused columns, missing and older anchors, ambiguous controls and totals, stamp invariance, aging with the control's own link;
  • a Hypothesis property over random states, values, US excess, controls, scales and feed orders, asserting 1–7.

test_us_acs_local_cd_properties.py extends the state_cd Hypothesis property with a national-share measure. It adds an example for the Wyoming/South Dakota case and a fail-closed check that an unbridged, unshared district-only level refuses.

Effect on the pinned feed

full, compiled at 2024 with aging and the packaged crosswalk. The dump-to-dump diff is in diff_dumps.out.

Before After
District-file amount rows (51 states, and 436 districts, each sum) $152.36B on interest_deduction (0.673x line 10) $186.31B on deductible_mortgage_interest (1.000x the national home mortgage target)
District-file return rows, each sum 11,352,500 (0.950x) 11,644,348 (1.000x)
California amount $38.13B $46.63B
Rebase factor, amount / returns — 1.222854 / 1.025708
Compiled specs 33,250 33,248 (the file's two US rows retire)
national_state 6,102 at e1f9906bf0e0 unchanged

No other spec changes in name, value or metadata. The share rows age with the national row's own 2023→2024 link (1.087213).

ACS-local state_cd (test_pinned_feed_state_cd_surface_matches_its_contract, re-pinned):

Before After
District-file state rows 302 304 (Wyoming and South Dakota interest amounts return)
District rows by parent basis 2,562 bridged 1,708 bridged, 854 national share
Total SOI specs 26,272 26,274
Interest amount state rows, sum 49 rows, $161.58B (bridged 8a, on interest_deduction) 51 rows, $186.31B (= the national control)
Interest return state rows, sum 11,514,598 11,644,348
Band drops 8 6 (the two interest bridges are gone)

Wyoming and South Dakota fell out of the itemized bridge's band because of charitable giving: HT2/CD A19700 is 2.10 and 1.98. Their line 8a agrees between the two files, at 1.007 and 1.028.

Interplay

axiom: n/a: microsim target mapping only (no policy encoded)

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 12, 2026 10:36
…#1038)

22incd.csv has no Schedule A line 10 column. Chronicle publishes its
line 8a column (N19300/A19300, "Mortgage interest paid") under
interest_paid_deduction_*, whose Table 2.1 rows are the line 10 total,
so the district file's rows bound interest_deduction at 0.673x the
national line 10 target on the full surface.

Each district-file row that reads N19300/A19300 now binds
deductible_mortgage_interest as its share of the file's US row times
Table 2.1's home mortgage interest control (8a + 8b), keyed by column
so a relabeled package compiles the same. A district interest_paid_*
fact reading any other column, or none, refuses the compile. The file's
US row retires; rows without an anchor are dropped. A district's share
of 8a is within 2.4% of its share of 8a + 8b (median 0.45%); as a proxy
for line 10 it is off by a median 7.3% and up to 54.6% (FL-21).

The ACS-local state_cd surface binds these share rows as compiled
instead of bridging them through itemized deductions, which would lift
them a second time and dropped Wyoming and South Dakota over their
charitable deductions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PolicyEngine/chronicle#345 relabels the district file's line 8a column
to mortgage_interest_paid_* and adds lines 8b, 8c and 9 under Table
2.1's own ids. The column-keyed rule binds the relabeled 8a rows
unchanged and the new ids stay unmapped, so nothing double-binds.
Also drop an unused import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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