Carry the pool's measurements on the solver input - #30
Merged
Merged
Conversation
pool_n_nodes told the solver worker that a round had more nodes for an
aircraft than the input it was handed uses, and nothing more. Measured
live tonight, 72% of published dark solves are narrower than their pool
(mean shortfall 2.27 nodes), and 390 of 481 rejected n=2 candidates had
a pool of 3 or more — a third node existed in the same round and would
have made an n=3 candidate, which publishes 81% of the time against 6%
for n=2. Acting on that requires the missing node's numbers, not just
its id: once the position clustering declines to merge a pairing, the
delay and Doppler it carried are unreachable downstream.
So the shared-track component now also collects its measurements, and
each input stamps pool_measurements: one {node_id, track_id, delay_us,
doppler_hz, snr, t_s} per pool node that is NOT already in the input.
A node the component reached on two different tracks is ambiguous by
construction (one of them is another aircraft), so the strongest SNR
wins and pool_conflicts counts how many nodes needed that tiebreak —
the rate at which a consumer's adoption gate is being handed a coin
flip, rather than a silent guess.
Nothing here decides whether widening is correct: that judgement needs
a solved position to predict against, and this stage has none. It only
makes the material reachable. Cost is one dict per pairing side in the
union-find pass already being made, and None (not []) whenever there is
no round to take a pool from, matching pool_n_nodes' "not measured".
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
format_track_pairs_for_solveralready stampspool_n_nodes/pool_node_ids— the node set of the input's shared-track component, i.e. the widest solve the round could have produced for that aircraft. This adds the other half:pool_measurements, one{node_id, track_id, delay_us, doppler_hz, snr, t_s}per pool node that is not already in the input'smeasurements, pluspool_conflicts.Why
Measured live tonight: 72% of published dark solves are narrower than their pool (mean shortfall 2.27 nodes). Published n=2 with pool>=3: 28 of 45. Published n=3 with pool>=4: 125 of 169. And 390 of 481 rejected
n2_unconfirmedcandidates had pool>=3 — a third node was available in the same round, and an n=3 candidate publishes 81% of the time against 6% for n=2.Widening the position merge radius is not the fix (a 6 km sweep raised cross-aircraft contamination from 25% to 32%). The fix is for the solver worker to adopt a pool node only when the solve itself vouches for it — predict what that node should measure at the solved position/velocity and check it agrees. That needs the missing node's actual numbers, and once the clustering declines to merge a pairing those numbers are unreachable downstream. This makes them reachable; it decides nothing.
How
_shared_track_poolsnow returns(node_ids_by_pair, measurements_by_pair). The measurement map is built in the union-find pass it was already making, keyed per component asnode_id → track_id → measurement, so it costs one dict per pairing side._solver_inputtakes the pool's measurements and emits only the spare part — pool nodes absent from the cluster. A node the component reached on two different tracks is genuinely ambiguous (one of them is another aircraft): the highest SNR wins andpool_conflictscounts how many nodes needed that tiebreak, so the coin-flip rate is visible instead of silent.Nonerather than[]when there was no round to take a pool from, matchingpool_n_nodes' "not measured".Tests
Three new cases in
TestSharedTrackPool: the missing node's delay/Doppler travels with the narrow input (and the wide sibling has nothing spare); a pool node seen on two tracks takes the strongest and setspool_conflicts; an input built with no round reportsNone.pytest tests --ignore=tests/test_empirical_coverage_fixes.py(shapely not installed in this env): 416 passed.ruff check/ruff format --checkclean.🤖 Generated with Claude Code