Skip to content

feat: add linfa-residual-sequence crate#430

Open
Feiyang472 wants to merge 19 commits intorust-ml:masterfrom
Feiyang472:compose
Open

feat: add linfa-residual-sequence crate#430
Feiyang472 wants to merge 19 commits intorust-ml:masterfrom
Feiyang472:compose

Conversation

@Feiyang472
Copy link

Hi! Wanted to float the idea of a small crate for chaining models together additively, fitting each model with the residuals of the previous one, like below

LinearRegression::default()
    .stack_with(some_svm)
    .stack_with(LinearRegression::default())
    .fit(&dataset)?

feels like a natural ergonomic addition which could sit as a crate in linfa.

Happy to hear if this belongs somewhere else (folded into an existing crate, or not at all).

Implements ResidualSequence Struct and StackWith trait for composing regression
models in a boosting / residual-stacking pattern. The second (and any
further) model trains on the residuals left by the previous one;
predictions are summed.

Docs and tests were written with AI assistance.
@codecov
Copy link

codecov bot commented Feb 27, 2026

Codecov Report

❌ Patch coverage is 88.46154% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.76%. Comparing base (b1f9ddb) to head (4a712e6).

Files with missing lines Patch % Lines
src/composing/residual_chain.rs 88.46% 6 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master     #430   +/-   ##
=======================================
  Coverage   76.76%   76.76%           
=======================================
  Files         105      106    +1     
  Lines        7359     7411   +52     
=======================================
+ Hits         5649     5689   +40     
- Misses       1710     1722   +12     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@relf
Copy link
Member

relf commented Mar 1, 2026

Hi! It looks like a nice addition! I think you should move it in the linfa crate under the existing composing module which gathers composite models.

@Feiyang472
Copy link
Author

Hi @relf, absolutely composing is the better place for this to be! I have moved it to composing/residual-sequence.

@Feiyang472 Feiyang472 marked this pull request as ready for review March 1, 2026 18:57
Copy link
Member

@relf relf left a comment

Choose a reason for hiding this comment

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

Ok, I think you should take a look at the contribution guidelines. Specially the sections about the implementation of PredictInplace trait and the optional serde support. As there is no hyperparameters (at the moment?), I guess you can skip the ParamGuard pattern.

where
Self: Fit<Array2<F>, Array1<F>, E>,
E: std::error::Error + From<crate::error::Error>;
}
Copy link
Author

Choose a reason for hiding this comment

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

could also add another method which just shrinks by F::one() (no shrinking). not sure how to name it

Copy link
Member

@relf relf Mar 6, 2026

Choose a reason for hiding this comment

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

Well, the .shrink_by(1.0) is noisy... With a conversion we could have:

let model = MeanParams.stack_with(MeanParams.into()));

otherwise you can add another method:

let model = MeanParams.stack_with_full(MeanParams);

It is better if the most common use case has the shorter name.
We could also have just stack(MeanParams) and stack_shrunk(MeanParams, 0.5).

Copy link
Author

Choose a reason for hiding this comment

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

Hi @relf thanks for reviewing. I propose the two methods:

chain(corrector: C)
chain_shrunk(corrector: Shrunk<C, F>)

I went with chain over stack since "stack" is so strongly associated with the fundamental data structure that it could be confusing in a library context. chain also echoes the ResidualChain struct name you suggested.

I kept chain_shrunk accepting a pre-wrapped Shrunk<C, F> rather than an inline shrinkage value (as in chain_shrunk(corrector, 0.5)) so that a Shrunk corrector can be constructed once and reused across multiple pipelines.

@Feiyang472 Feiyang472 requested a review from relf March 5, 2026 18:23
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.

2 participants