Skip to content

fix(lscp): keep n_bins and random_state as given, resolve them in fit - #778

Open
choksi2212 wants to merge 1 commit into
yzhao062:developmentfrom
choksi2212:fix/lscp-fit-parameter-overwrites-754
Open

choksi2212 wants to merge 1 commit into
yzhao062:developmentfrom
choksi2212:fix/lscp-fit-parameter-overwrites-754

Conversation

@choksi2212

Copy link
Copy Markdown

Closes #754

What happened

LSCP.fit wrote two derived values back onto its constructor parameters:

  • self.random_state = check_random_state(self.random_state) replaced the seed the user passed with a live generator object
  • when n_bins > n_clf, the clamp overwrote self.n_bins with n_clf

After one fit, get_params() no longer described the estimator the user built, so clone() produced a different configuration:

>>> clf = LSCP(detector_list=[LOF(), KNN()], n_bins=10, random_state=0)
>>> clf.fit(X)
>>> clf.get_params(deep=False)['n_bins']        # 2, not 10
>>> clf.get_params(deep=False)['random_state']  # RandomState(MT19937), not 0

The fix

Mirrors the Sampling/KPCA fix from #753: constructor parameters are kept as given, and the resolved values become fitted attributes:

  • random_state_ holds the generator used for feature subspace selection in _get_local_region
  • n_bins_ holds the effective bin count after the clamp
  • the n_bins reduction warning moves into fit, so it fires once per fit instead of once per test instance (it previously warned inside _get_competent_detectors, which runs per instance: a 60-row predict emitted 60 identical warnings)
  • RandomState instances and None keep their advancing-generator behavior, matching the Keep random_state as given in Sampling and KPCA #753 contract

The clamp semantics themselves are unchanged: a clamped fit scores identically to before, because np.histogram(scores, bins=n_clf) is the same call the old inline clamp made.

Tests

pyod/test/test_lscp_contract.py: 10 tests over two classes.

Negative control (proven both directions): with lscp.py reverted to pristine development, 8 of the 10 fail with the exact issue symptoms (get_params leaks the generator, n_bins reads back as 2, clone inherits the advanced generator, no fitted attributes); the other 2 are behavior-equivalence tests that pass on both (warning dedup + scores unchanged). All 10 pass on the fix.

Full suite: 1250 passed, 19 failed, 10 skipped locally - every failure is a missing optional dependency in my venv (torch for the deep-model and GAAL modules, suod for the SUOD tests), none related to LSCP. The pre-existing test_lscp.py (30 tests) and test_save_load_clone.py (LSCP clone/save/load round-trips) are fully green, and ruff check reports the same 6 pre-existing complaints on pristine development as on the fix (nothing new introduced).

Notes

While tracing the call flow I noticed _get_local_region also mutates self.local_max_features and self.local_min_features when they fall outside their valid ranges - the same parameter-overwrite defect class. It is not part of this fix (the issue scopes #754 to n_bins/random_state), but worth a follow-up issue if the maintainers agree the contract should hold there too.

LSCP.fit wrote two derived values back onto its constructor parameters:
check_random_state(self.random_state) replaced the seed with a live
generator (lscp.py:157) and the n_bins > n_clf clamp overwrote n_bins
(lscp.py:379-383). After one fit, get_params() no longer described the
estimator the user built and clone() produced a different configuration.

Mirror the Sampling/KPCA fix (yzhao062#753): the parameters are kept as given and
the resolved values become fitted attributes. random_state_ holds the
generator used for feature subspace selection; n_bins_ holds the
effective bin count after the clamp, which also moves the reduction
warning into fit so it fires once per fit instead of once per test
instance (it previously warned per _get_competent_detectors call).
RandomState instances and None keep their advancing-generator behavior.

Closes yzhao062#754

Signed-off-by: choksi2212 <choksi2212@users.noreply.github.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-10T10:39:43.805320Z 84771a4 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@choksi2212

Copy link
Copy Markdown
Author

Hi, gentle nudge on this one — it has been sitting green (12/12 checks) for about five days now.

Quick summary of the change: n_bins and random_state stay exactly as the user passed them, and everything that used to be guessed at construction time is resolved in fit instead. The two regression tests fail on current main with the exact symptom described in the discussion and pass with the patch.

Happy to rebase or reshape anything if you would like it done differently.

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