Skip to content

Choose the Gauss triplet by time span rather than first/middle/last - #533

Merged
matthewholman merged 3 commits into
mainfrom
feat/509-gauss-span-triplet
Sep 3, 2026
Merged

Choose the Gauss triplet by time span rather than first/middle/last#533
matthewholman merged 3 commits into
mainfrom
feat/509-gauss-span-triplet

Conversation

@matthewholman

Copy link
Copy Markdown
Collaborator

Gauss truncates the Lagrange f and g series, so the interval between the outer two observations has to be short against the orbital period -- classically well under 60 degrees of mean anomaly. We were taking the first, middle and last observation of seq[0], and seq[0] is by construction the longest-span chunk, so on a long arc the triplet was as wide as it could possibly be. Objects that fail to converge cold span a median 53 degrees of mean anomaly against 29 for those that fit.

This picks the triplet whose outer span is nearest a target instead.

The target is in mean anomaly, which needs a period, which needs the orbit we are trying to find. It is converted to days using an assumed a = 2.5 au rather than the object's own -- about 60 days -- which keeps it a prior rather than an oracle. Using each object's published a is slightly worse, so there is no chicken-and-egg problem here.

A balance guard requires each sub-interval to be at least 10% of the outer span. Without it a repeated epoch can leave the middle observation sitting on an endpoint, giving a zero-length interval and no usable root. Where no triplet qualifies, the old first/middle/last choice is used, so behaviour is never worse than before.

Measured cold on objects from the MPC catalog:

old this PR
objects the old selection already fit 400/400 398/400 (99.50%)
objects it did not (flag 3) 0/46 39/46 (84.8%)

Net 39 gained against 2 lost. The orbits are the same ones -- median |da/a| against the old selection is 1.7e-4 -- so this is a convergence improvement, not a different answer. Median runtime 2.8 s on the regression sample.

Worth noting for the issue: #509 proposed pooling several spans and trying each. We measured that at -13.9 points, nearly 5x the cost. Choosing the interval beats trying every interval.

Tests: 87 pass (36 IOD/Gauss, 51 wider fit suite).

Closes #509.

Gauss truncates the Lagrange f and g series, so the interval has to be short
against the orbital period. The triplet was the first, middle and last
observation of seq[0], and seq[0] is by construction the longest-span chunk, so
on a long arc it was as wide as it could be. The objects that fail to converge
cold span a median 53 degrees of mean anomaly against 29 for those that fit.

Pick the triplet whose outer span is nearest a target instead. The target is in
mean anomaly, which needs a period, which needs the orbit being sought -- so it
is converted to days with an ASSUMED a = 2.5 au rather than the object's own,
about 60 days. That keeps it a prior and not an oracle; using each object's
published a is slightly worse.

A balance guard requires each sub-interval to be at least 10% of the outer span.
Without it a repeated epoch can put the middle observation on an endpoint, which
gives a zero-length interval and no usable root. Where no triplet qualifies the
old first/middle/last choice is used, so behaviour is never worse than before.

Measured cold on objects from the MPC catalog:

  regression, objects the old selection already fit   398/400   (99.50%)
  residue, objects it did not                          39/46    (84.8%)

Net 39 gained against 2 lost. The orbits are the same ones -- median |da/a|
against the old selection is 1.7e-4 -- so this is a convergence improvement
rather than a different answer.

Closes #509.
The span selection exists to shorten over-wide triplets on long arcs. When the
whole segment already fits inside the ~60 day target there is nothing to
shorten -- the widest triplet is the best available, which is what
first/middle/last takes anyway -- so the only thing the selection could change
is the middle observation, which is not what it is for.

On a short arc that is pure downside. The 3I/ATLAS fixture spans 19 days
against the 60 day target, so both selections take the same outer pair; moving
the middle point alone shifted the fit epoch by 8 days and cost 1% in position
and 1.5% in velocity against JPL Horizons, on a weakly-constrained hyperbolic
orbit. Caught by tests/layup/test_3i_atlas_validation.py, which now passes.

The long-arc behaviour is unchanged, so the measured results stand: the
regression sample is objects the old selection already fit, and the residue
objects are long-arc by construction.
The fit epoch is the middle observation of the selected triplet, so choosing
the triplet by time span moves it. Two of the three real-data objects shift:
119839 by 87 days and 742428 by 11 days. 609631 and 3I/ATLAS are unchanged
once the short-arc guard is in.

Both references were re-queried from JPL Horizons at the new epochs, at the
full precision the epoch assertion needs, using the recipe recorded in the
file's own _comment_.

The fits were checked against the new states BEFORE they were stored: 119839
agrees to 3.8e-7 and 742428 to 3.9e-7 in relative position. So this is an
epoch change, not a loosened comparison -- which matters, because regenerating
a reference is exactly how a real regression gets hidden. That is not
hypothetical here: before the short-arc guard, 3I/ATLAS was off by 1% in
position, and regenerating its reference would have made that pass silently.

@kjnapier kjnapier left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting

@matthewholman
matthewholman merged commit 19456eb into main Sep 3, 2026
9 of 11 checks passed
@matthewholman
matthewholman deleted the feat/509-gauss-span-triplet branch September 3, 2026 19:14
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.

gauss_iod picks the first, middle and last observation, which is too wide on long arcs

2 participants