fix(upstream): stop FixDupsIter replaying the final training row#44
Conversation
FixDupsIter's terminal branch returned `self.last_item.take()`, which re-emitted the already-yielded final row. `RMITrainingData::iter()` therefore produced len()+1 items, so every active-path `LinearModel` leaf/routing fit double-weighted its last point in `slr`. Return `None` at exhaustion instead. Add regression tests pinning one-item-per-row iteration and duplicate-key collapsing, document FixDupsIter's contract, and drop the now-stale replay-workaround note in the lower-bound test. Inherited from learnedsystems/RMI; also fixed upstream in learnedsystems#15. Refs #3
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
Walkthrough
ChangesFixDupsIter exhaustion fix
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
FixDupsIter(behindRMITrainingData::iter()) re-emitted the already-yielded final row in its terminal branch (None => self.last_item.take()), soiter()producedlen()+1items for any input — the last(key, offset)pair appeared twice.Unlike most of the latent upstream bugs tracked in #3, this one is on the active trainer path:
train_with_configfits every L2 leafLinearModel(trainer.rs:335) and the routingLinearModel(trainer.rs:478) viadata.iter()(LinearModel::new,linear.rs:177), so eachslrfit double-weighted its final point. The bias is small for large leaves and grows as leaves get small. (LowerBoundCorrection::newalso iteratesdata.iter(), but the phantom only inflatedlongest_runby 1 — which the active path doesn't use — and left first/last/next/prev unchanged.)Fix
Return
Noneat exhaustion instead of replaying. Also:FixDupsIter's contract (collapse duplicate-key runs onto the first occurrence's offset; yield exactly one item per input row).iter()reproduces the input one-to-one (no trailing replay), and a duplicate-key run collapses onto the first offset while keepinglen()unchanged.single_run_reports_full_lengthtest (that test derives its expected count fromdata.iter(), so it still passes).Validation
cargo testsuite passes (no existing golden-value test depended on the phantom row — the perfect-linear-fit trainer tests are unaffected, since duplicating a point already on the line doesn't change the fit).cargo clippy -p prmi --all-targetsclean.Context
Inherited verbatim from
learnedsystems/RMI; also fixed upstream in learnedsystems#15 (which revived that repo's bit-rotted unit tests — the failures are what surfaced this bug). Tracked in #3.Refs #3
Summary by CodeRabbit