fix: make location spacing order-independent - #22
Open
ChrisW09 wants to merge 1 commit into
Open
Conversation
``BaseLocationSelector._enforce_spacing`` compared each candidate only against the most recently kept one (``point - spaced[-1] >= min_distance``), which is correct only for ascending input. ``LightGBMLocationSelector`` deliberately returns candidates in gain-descending order, as the base-class docstring requires, so every candidate positioned below the previous keeper produced a negative difference and was dropped. On a 2000-point sin(x) fit over [0, 10] this collapsed 184 candidates to 8, all inside [6.27, 9.82], and because the survivors came out ascending the subsequent ``points[:max_count]`` trim then kept the numerically lowest thresholds instead of the highest-gain ones -- discarding the gain ranking that motivates the lightgbm strategy in the first place. Compare against every kept location instead. For ascending input this is equivalent to the previous test, so the single-tree path is unchanged; the gain-ordered path now retains 65 candidates and its trim is meaningful again. Closes #10 Co-Authored-By: Claude Opus 5 (1M context) <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.
Fixes #10.
Problem
BaseLocationSelector._enforce_spacingtests each candidate withpoint - spaced[-1] >= min_distance, which is only meaningful for an ascending list.LightGBMLocationSelector._ordered_candidatesreturns candidates in gain-descendingorder — the base-class docstring at
selectors.py:121-126states that is the contract — soany candidate positioned below the previous keeper yielded a negative difference and was
silently dropped.
Two consequences, both silent:
x ~ U(0, 10),y = sin(x),184 candidates became 8, all inside
[6.27, 9.82]._trim_over_max'spoints[:max_count]then kept the numerically lowest thresholds rather than the highest-gain ones,
discarding the gain ranking that is the entire reason to choose
lightgbmovercart.Fix
Compare each candidate against every location kept so far rather than only the last one,
making the filter independent of arrival order.
For already-ascending input the two tests are equivalent —
spaced[-1]is the nearest keptlocation — so the single-tree path is bit-for-bit unchanged. The gain-ordered path now keeps
its ordering, which also makes the existing
points[:max_count]trim correct withouttouching it.
Result
select(min=3, max=8)[6.27 … 9.82][2.84 … 9.14]PLE thresholds through the
Preprocessor(feature scaled to[-1, 1]):The remaining clustering in the
lightgbmrow is now genuine gain ranking (the steepestparts of
sin), not an artifact of the filter.Tests
Five added to
tests/test_location_selectors.py:test_enforce_spacing_is_order_independent— ascending and shuffled input give the same settest_enforce_spacing_still_drops_close_neighbours— the spacing guarantee still holdstest_enforce_spacing_matches_legacy_result_on_ascending_input— pins the no-changeguarantee for the single-tree path against a literal copy of the old loop
test_lightgbm_locations_cover_the_feature_range— regression test for the reported bugtest_lightgbm_keeps_the_highest_gain_locations— gain order survives the filterFull suite: 485 passed, 9 xfailed.
ruff checkclean on both changed files; pyrightunchanged at its pre-existing 71 errors.
🤖 Generated with Claude Code