Open-source project
perpetual-ml/perpetual avatar
perpetual-ml/perpetual

Perpetual: a gradient boosting machine with a budget parameter instead of a tuning loop

Perpetual is a high-performance gradient boosting machine. It delivers optimal accuracy in a single run without complex tuning through a simple budget parameter. It features out-of-the-box support for causal ML, continual learning, native calibration, and robust drift monitoring, along with Rust core and zero-copy bindings for Python and R

707 stars42 forksRustApache-2.0

At a glance

What is it?
Perpetual replaces hyperparameter search with a single budget value and ships causal ML, drift monitoring, calibration and continual learning in one Rust core with Python and R bindings. The trade-off is a young 3.0 release candidate line and a tuning model that assumes you trust the library's internal choices.
Who is it for?
Adopt Perpetual if you want a GBM whose only dial is budget and you need causal effect estimation, drift monitoring or calibrated distributions in the same package. Do not adopt it if you depend on a long-stable API or you need to reproduce published results from a specific older version.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 166 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem Perpetual targets: tuning loops that cost more than the model

Gradient boosting libraries usually hand you a search space. You pick a learning rate, a depth, a number of rounds, a subsample fraction, then you run a search and hope the best cross-validation score transfers to unseen data. Perpetual's README frames the alternative directly: it is a GBM that "doesn't need hyperparameter optimization unlike other GBMs", and the interface it offers instead is a single budget parameter. The intended audience is the practitioner who has features they trust and wants a defensible model without spending a week on a search. The README's own guidance is to "Start with a small budget (e.g. 0.5) and increase it (e.g. 1.0) once you are confident with your features", and it adds a diagnostic: if raising the budget stops helping, the data is already giving up what it has. That is a different mental model from tuning. You are not searching for a configuration, you are buying compute until the marginal gain disappears. It suits tabular problems where feature engineering is the real work and the booster is a commodity. It suits less well anyone whose acceptance criteria include matching a specific published configuration, because there is no configuration to match.

One budget parameter, and what the library does with it

The README does not publish the internal objective that budget optimizes, so the honest description is at the level the documentation supports. Budget is a compute allowance: increasing it "increases the predictive power of the algorithm and gives better results on unseen data", according to the README. That phrasing implies the library is spending the budget on its own structural decisions rather than exposing them. What the README does list as native tree behaviour is concrete: categorical variables handled natively, learnable missing value splits, monotonic constraints, and feature interaction constraints. Those are the knobs that normally require careful encoding or per-column configuration in other libraries, and here they are described as built in. The supported objectives are binary classification, multi-class classification, regression and ranking. Explainability is listed as feature importance, partial dependence plots and Shapley values. None of this is unusual for a modern GBM. The unusual part is that none of it is presented as something you tune. If you are the kind of engineer who wants to know why a split was chosen, the README points you at SHAP rather than at a parameter you set.

Causal ML, drift monitoring and calibration in the same binary

Three features in the README are not standard in a boosting library and are worth separating from the marketing list. First, causal ML: "out-of-the-box support for causal machine learning to estimate treatment effects". Treatment effect estimation normally means a separate package with its own estimator classes. Second, drift monitoring: the README claims built-in monitoring of both data drift and concept drift "without requiring ground truth labels or model retraining". Concept drift detection without labels is the harder claim of the two, and the README gives no method. Third, calibration: "predict fully calibrated distributions (marginal coverage) and conditional coverage without retraining". Calibration is usually a post-hoc step with its own held-out set. Putting these three in the same library means one dependency instead of four, and one model object that carries the monitoring and calibration state. It also means the quality of each is bounded by the same release cycle. A drift monitor that ships with the booster cannot be swapped for a better one without leaving the library. That is the cost of the integration, and the README does not discuss it.

Getting it running: pip, conda, cargo or install.packages

The installation surface is small. Python: pip install perpetual, or conda install -c conda-forge perpetual. Rust: cargo add perpetual. R: install.packages("perpetual"). Two optional Python dependencies are named in the README: pandas, which "Enables support for training directly on Pandas DataFrames", and polars, which "Enables zero-copy training support for Polars DataFrames". The zero-copy path matters if your data already lives in Arrow-backed frames, because it avoids a conversion step before training. The README does not show a training snippet in the excerpt provided, so the parameter names beyond budget cannot be quoted here. What can be quoted is the budget workflow: start at 0.5, move to 1.0 when features are settled, and stop raising it when results flatten. Documentation links are split by language: the Python API reference, docs.rs for the Rust crate, and a pkgdown site for R. For deployment, the README states models can be exported "to industry-standard XGBoost or ONNX formats", which is the escape hatch if your serving stack already speaks one of those.

Continual learning and the O(n squared) to O(n) claim

The continual learning entry is the most specific performance claim in the README: it says the feature reduces computational time "from O(n²) to O(n)". That is a complexity statement, not a benchmark, and the README does not say what n counts (rows, rounds, or update steps) or under what conditions the reduction holds. Treat it as a direction rather than a number. The practical question for anyone considering it is what happens to the model when new data arrives: does the update preserve the calibration and drift state, and does the budget need to be re-chosen? The README does not answer either. Continual learning is also where a boosting library's assumptions are most likely to break, because trees fitted on old data may no longer describe the new distribution, and the drift monitoring feature exists precisely because that can happen. Shipping both in one library is coherent. Whether the update path is safe for your data is something only a local experiment will tell you, and nothing in the supplied material substitutes for that.

Version risk: 3.0.0 is still a release candidate

The most recent releases listed are v3.0.0-rc.2, v3.0.0-rc.1 and v3.0.0-rc.0, all from March and April 2026. The repository is not archived and the last push is recent, so the project is active. But the head of the release line is a release candidate, which means the API described in the README may still move before 3.0.0 is final. For a library that positions itself as production ready and interoperable, that is the tension to weigh. If you pin to a stable 2.x release, you may not get the feature set the README describes. If you track the release candidates, you accept that a minor version bump can change behaviour. The README does not include a changelog or a compatibility statement in the supplied text, so the upgrade cost between candidates cannot be estimated from this material. Anyone evaluating Perpetual should check the release notes for each candidate before deciding which one to build against.

The limitation: no tuning also means no override

Hyperparameter-free is a real benefit and a real constraint. If the budget-driven internal choices produce a model that fails your validation, the README offers no documented way to intervene at the level of tree structure. The listed tree features (categorical handling, missing value splits, monotonic and interaction constraints) are the available controls, and they are constraints on the model rather than parameters of the fitting process. That is a narrower escape hatch than a learning rate or a max depth. The second limitation is the drift monitoring claim. Concept drift detection without ground truth labels is a hard problem, and the README states the capability without describing the mechanism, so a reader cannot judge its sensitivity or its false positive behaviour. The third is that a single budget value is not a single cost: budget 1.0 on a wide dataset is not the same wall-clock time as budget 1.0 on a narrow one, and the README gives no scaling guidance. None of these are disqualifying. They are the places where you have to run your own experiment before trusting the library with a production model.

How this differs from XGBoost and LightGBM

The comparison the README invites is against the mainstream GBMs, and the difference is in the interface, not the algorithm family. XGBoost and LightGBM expose the search space and expect you to search it, with early stopping as the main automated control. Perpetual removes the search space and exposes a budget, which is closer in spirit to an AutoML wrapper than to a raw booster. That means the two approaches fail differently. With XGBoost or LightGBM, a bad model usually means a bad search, and you can inspect the parameters that produced it. With Perpetual, a bad model means either the budget was too low or the data does not support the problem, and the README's diagnostic (raise the budget, watch for flattening) is the tool you have. The other difference is scope. Causal effect estimation, drift monitoring and calibration are separate libraries in the XGBoost and LightGBM ecosystems. Perpetual bundles them, which reduces integration work and increases the cost of replacing any single piece. Export to XGBoost or ONNX softens that, since a trained model can leave the library for serving, but the monitoring and calibration state does not travel with it.

Editorial conclusion

Adopt Perpetual if you want a GBM whose only dial is budget and you need causal effect estimation, drift monitoring or calibrated distributions in the same package. Do not adopt it if you depend on a long-stable API or you need to reproduce published results from a specific older version. Before committing, verify which release candidate you are installing, whether your deployment path accepts the ONNX or XGBoost export, and how continual learning behaves on your own data, since the README describes the complexity change but gives no worked example.

Official sources

  1. License: Apache-2.0
  2. perpetual-ml/perpetual on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes