Rust-first gradient boosting for regression, classification, and ranking with time-aware validation and Python bindings
Project description
AlloyGBM
AlloyGBM is a Rust-first gradient boosting library with Python bindings, supporting regression, binary and multi-class classification, and learning-to-rank. It is built for fast native execution, deterministic training, and time-aware tabular workflows.
AlloyGBM is strongest on panel and finance-style problems where leakage-aware validation and practical iteration speed matter. It also performs competitively on general tabular benchmarks and includes native artifact prediction, TreeSHAP explanations, and purged time-series split helpers.
When To Use AlloyGBM
AlloyGBM is a good fit when you want:
- a native Rust-backed gradient boosting library with regression, classification, and ranking
- deterministic CPU training and inference
- sklearn-compatible estimators (
GBMRegressor,GBMClassifier,GBMRanker) - time-aware validation helpers for forecasting or panel-style workflows
- native prediction from serialized artifacts
- TreeSHAP explanations and global feature importances
- NaN/missing value support out of the box
- model persistence via pickle, save/load, or artifact export
Installation
PyPI:
pip install alloygbm
From source:
python -m pip install --upgrade maturin
maturin develop --manifest-path bindings/python/Cargo.toml --release
AlloyGBM targets Python 3.11+ and uses a native Rust extension module.
Wheel targets for 0.12.10:
- macOS
arm64 - Linux
x86_64(manylinux) - source distribution for other platforms
Quick Examples
Regression
from alloygbm import GBMRegressor, rmse
model = GBMRegressor(
learning_rate=0.05,
max_depth=6,
n_estimators=1200,
deterministic=True,
seed=7,
)
model.fit(X_train, y_train, eval_set=(X_valid, y_valid))
print(rmse(y_test, model.predict(X_test)))
Binary Classification
from alloygbm import GBMClassifier, accuracy, log_loss
model = GBMClassifier(
learning_rate=0.05,
max_depth=6,
n_estimators=500,
deterministic=True,
seed=7,
)
model.fit(X_train, y_train)
labels = model.predict(X_test) # [0, 1, 1, 0, ...]
probas = model.predict_proba(X_test) # [[P(0), P(1)], ...]
print("accuracy:", accuracy(y_test, labels))
print("log_loss:", log_loss(y_test, probas[:, 1]))
Learning-to-Rank
from alloygbm import GBMRanker, ndcg
model = GBMRanker(
ranking_objective="rank:ndcg",
learning_rate=0.05,
max_depth=6,
n_estimators=300,
deterministic=True,
seed=7,
)
model.fit(X_train, y_train, group=query_ids_train)
scores = model.predict(X_test)
print("NDCG@10:", ndcg(y_test, scores, group=query_ids_test, k=10))
MorphBoost (Adaptive Split Criterion)
MorphBoost is an opt-in training mode that blends the standard gradient gain
with a normalized information-theoretic term. Across rounds, the blend ramps
in via a tanh(iter/20) warmup, an EMA over per-class gradient statistics
shapes split selection, and leaf magnitudes are scaled by a depth penalty
and per-iteration shrinkage. See the
MorphBoost paper for the formulation.
from alloygbm import GBMRegressor
# Constant LR (default) with morph adaptive split criterion
model = GBMRegressor(
n_estimators=1200,
max_depth=6,
learning_rate=0.05,
training_mode="morph", # opt in
morph_rate=0.1, # per-round leaf shrinkage
info_score_weight=0.3, # blend weight for info-theoretic term
depth_penalty_base=0.9, # multiplier per depth level
balance_penalty=True, # penalize highly imbalanced splits
seed=7,
)
model.fit(X_train, y_train)
# With warmup-cosine LR schedule (good fit for very-low-LR runs)
model = GBMRegressor(
n_estimators=5000,
learning_rate=0.01,
training_mode="morph",
lr_schedule="warmup_cosine",
lr_warmup_frac=0.1, # fraction of n_estimators spent in warmup
seed=7,
)
training_mode="morph" works with GBMClassifier and GBMRanker too, with
identical parameter semantics.
DRO Leaf Solver (Robust Scalar Leaves)
Set leaf_solver="dro" to use a fast Wasserstein-inspired robust Newton update
for scalar leaves. The solver penalizes each candidate leaf by within-leaf
gradient dispersion, reducing sensitivity to noisy or weak leaf signals while
keeping prediction speed identical to standard constant leaves.
from alloygbm import GBMRegressor
model = GBMRegressor(
n_estimators=600,
max_depth=6,
learning_rate=0.05,
leaf_solver="dro",
dro_radius=0.05,
dro_metric="wasserstein",
seed=7,
)
model.fit(X_train, y_train)
leaf_solver="dro" works with GBMRegressor, GBMClassifier, and
GBMRanker, and composes with training_mode="morph". It requires
leaf_model="constant"; piecewise-linear leaves still use the standard PL
solver. dro_radius=0.0 preserves standard-leaf predictions while retaining
DRO metadata in the artifact.
Factor-Neutral Boosting
Use neutralization="per_round_gradient" with fit(..., factor_exposures=F) to project each boosting round's pseudo-residuals away from user-supplied nuisance factors. This is useful when common factors explain high-variance signal that you do not want the model to spend tree capacity learning.
This is a training-time regularization tool. It does not guarantee prediction-time zero exposure unless predictions are neutralized against evaluation-time factors outside the model.
Constructor parameters:
GBMRegressor(
neutralization="none", # "none" | "pre_target" | "per_round_gradient" | "split_penalty"
factor_neutralization_lambda=1e-6, # finite, >= 0 ridge added to F^T W F
factor_penalty=0.0, # finite, >= 0; only active for neutralization="split_penalty"
factor_exposure_transform="none", # "none" | "center" | "standardize"
)
factor_exposures is dense, row-major, finite, and shaped
(n_rows, n_factors). It is fit data, not constructor state, so sklearn
cloning remains clean and large matrices are not embedded in estimator params.
Set factor_exposure_transform="center" or "standardize" to apply column-wise
preprocessing before the projector/penalty calculation; fit-time means and
standard deviations are recorded in factor_exposure_diagnostics_.
Mode semantics:
neutralization="none" preserves current behavior and ignores
factor_exposures unless a non-None matrix is provided with an inactive mode,
in which case Python raises a clear validation error to prevent silent user
mistakes.
neutralization="pre_target" residualizes the regression target once before
training:
y_perp = y - F (F^T W F + lambda I)^-1 F^T W y
This mode is supported for GBMRegressor only. It is rejected for
classification and ranking because target residualization is not well-defined
for class labels or ranking relevance. eval_set is also rejected for
pre_target in this release because the public API does not yet accept
validation-set factor exposures to residualize validation targets consistently.
neutralization="per_round_gradient" projects objective gradients before each
boosting round:
g_perp = g - F (F^T W F + lambda I)^-1 F^T W g
Hessians are unchanged. This mode is supported for regression, binary classification, multiclass, and ranking. For multiclass, each class-gradient column is projected independently against the same factor projector.
neutralization="split_penalty" includes per-round gradient projection and
subtracts a factor-load penalty from split gain:
penalty = factor_penalty * || F_L^T update_L + F_R^T update_R ||^2 / max(row_count, 1)
gain_final = gain_after_existing_modes - penalty
For scalar leaves, update_L and update_R are the candidate scalar leaf
values before any final MorphBoost depth/iteration leaf scaling. For DRO
leaves, the scalar values use the DRO effective gradients. For MorphBoost, the
order is: project gradients, compute standard/DRO gradient gain, blend
MorphBoost information score, subtract factor penalty, then apply MorphBoost
leaf scaling when storing leaves. split_penalty performs additional
factor-exposure work during split search and should be treated as the slowest
neutralization mode until production-scale benchmarks justify stronger claims.
Compatibility:
| Feature | pre_target | per_round_gradient | split_penalty |
|---|---|---|---|
GBMRegressor |
supported | supported | supported |
GBMClassifier |
rejected | supported | supported |
GBMRanker |
rejected | supported | supported |
training_mode="morph" |
supported | supported | supported |
leaf_solver="dro" |
supported | supported | supported |
leaf_model="linear" |
supported | supported | rejected |
| warm start | supported | supported | supported |
Exposure matrices are not persisted in the estimator or artifact. As of
v0.7.1, neutralized warm-start and init_model continuation are supported
across all three modes provided the caller supplies the same
factor_exposures matrix used for the initial fit; neutralization,
factor_neutralization_lambda, and (for split_penalty) factor_penalty
must match the persisted contract — mismatches raise a clear "does not
match" error.
Piecewise-Linear Leaves
Set leaf_model="linear" on any estimator to replace scalar leaves with small
closed-form linear models (f_s(x) = b_s + Σ α_j x_j). Weights are solved via
ridge regression α* = -(XᵀHX + λI)⁻¹ Xᵀg regularised by lambda_l2. This
typically converges in fewer rounds on data with linear within-node residual
structure (e.g. California Housing), at a 2–8× per-round training overhead.
from alloygbm import GBMRegressor
model = GBMRegressor(
n_estimators=300,
max_depth=6,
learning_rate=0.05,
leaf_model="linear",
lambda_l2=0.01, # recommended >= 0.01 with linear leaves
seed=7,
)
model.fit(X_train, y_train)
leaf_model="linear" works with GBMClassifier and GBMRanker, and composes
with training_mode="morph". As of v0.7.1, SHAP works on leaf_model="linear"
artifacts as a best-effort interventional decomposition (exact additivity is
relaxed for continuous-feature PL artifacts; see
docs/limitations.md).
Time-Aware Validation
from alloygbm import GBMRegressor, purged_time_series_splits, rmse
splits = purged_time_series_splits(time_index, n_splits=5, purge_gap=1, embargo=0)
for train_idx, test_idx in splits:
model = GBMRegressor(deterministic=True, seed=7)
model.fit(
[rows[i] for i in train_idx],
[targets[i] for i in train_idx],
)
score = rmse(
[targets[i] for i in test_idx],
model.predict([rows[i] for i in test_idx]),
)
For panel data, use purged_panel_splits(...).
Model Persistence
import pickle
# Pickle round-trip
with open("model.pkl", "wb") as f:
pickle.dump(model, f)
with open("model.pkl", "rb") as f:
model = pickle.load(f)
# Native save/load
model.save_model("model.agbm")
loaded = GBMRegressor.load_model("model.agbm")
# Artifact export for deployment
artifact_bytes = model.artifact_bytes
Feature Summary
Estimators
GBMRegressor-- regression with dataset-awaretraining_policy. As of v0.12.7 supports six built-in objectives:"squared_error"(default),"poisson","gamma","tweedie"(withtweedie_variance_power∈ (1, 2)),"quantile"(withquantile_alpha∈ (0.0, 1.0)), and custom callables. All three GLM objectives use a log-link (predict()returnsexp(raw)).GBMClassifier-- binary classification with log-loss objective,predict_proba, sklearnClassifierMixinGBMRanker-- learning-to-rank with 5 objectives:rank:pairwise,rank:ndcg,rank:xendcg,queryrmse,yetirank. As of v0.12.8 also accepts the GLM/quantile regression objectives (poisson,gamma,tweedie,quantile) viaranking_objective=.MultiLabelGBMRanker-- multi-output ranking:yshaped(n_rows, n_labels),predictreturns the same shape, per-labelranking_objectivelists supported. As of v0.10.1 also supportsmulti_label_mode="joint"for shared-tree training across all K labels viaengine::joint::fit_joint_multi_output(default"independent"preserves the K-per-labelGBMRankerfallback). v0.10.2 expanded joint-mode kwargs to includetree_growth="leaf"+max_leaves,interaction_constraints,min_split_gain,row_subsample, andcol_subsample. v0.10.3 wires native-categorical splits (categorical_feature_indices+max_cat_threshold) through the joint Python bridge, addsboosting_mode="goss"andboosting_mode="dart"to the joint trainer, and supportswarm_start=True+init_model=...on the joint path. v0.10.4 adds MorphBoost to the joint trainer (training_mode="morph"+ the fullmorph_*/lr_schedulesurface, with EMA warm-resume via theMorphMetadataartifact section). v0.10.5 adds joint DRO leaves (leaf_solver="dro"+dro_radius/dro_metric). v0.10.6 closes the last v0.10.4-deferred follow-up by adding all three factor-neutralization modes (neutralization="pre_target" | "per_round_gradient" | "split_penalty"+factor_exposures=onfit()) to the joint trainer — full feature parity with the single-output path.- All estimators are sklearn-compatible (
get_params,set_params,score, pipeline integration)
Training Features
- NaN/missing value support with learned split direction
- Sample weights via
fit(..., sample_weight=...) - Monotone constraints via
monotone_constraints - Feature importance weighting via
feature_weights - Leaf-wise (best-first) tree growth via
tree_growth="leaf" - Warm-starting / incremental training via
warm_start=True - Up to 65,535 bins per feature (
continuous_binning_max_bins) - Multiple categorical column support via
categorical_feature_indices - Early stopping with
best_iteration_,best_score_,evals_result_ - Objective-aware training metric tracking (RMSE, log-loss, accuracy, NDCG)
- Adaptive split criterion via
training_mode="morph"(MorphBoost) - Per-iteration learning-rate schedules:
lr_schedule="constant"(default) or"warmup_cosine" - DRO-style robust scalar leaves via
leaf_solver="dro"(closed-form gradient-uncertainty penalty) - GOSS (gradient-based one-side sampling, LightGBM-style) via
boosting_mode="goss"+goss_top_rate/goss_other_rateon regression, binary classification, and ranking. As of v0.10.1, GOSS is also supported on multiclass classification (K ≥ 3 classes) — per-row scores_i = Σₖ |g_{i,k}|(LightGBM convention) drives a shared sampling mask across all K class gradient buffers. Defaultboosting_mode="standard"is byte-identical to v0.7.5. - DART (Dropouts meet MART) via
boosting_mode="dart"+dart_drop_rate/dart_max_drop/dart_normalize_type("tree"or"forest") /dart_sample_type("uniform"or"weighted") on regression, binary classification, and ranking. Per-stump weights ride in a newDartTreeWeightsartifact section emitted only when at least one stump diverges fromtree_weight = 1.0, so Standard / GOSS artifacts stay byte-identical to v0.8.0. DART +warm_startcontinuation is supported (v0.10.0+) — pass a fitted DART model viafit(..., init_model=prior_model)to add more rounds on top. As of v0.10.1, DART is also supported on multiclass classification (K ≥ 3 classes) including warm-start. v0.10.2 lifts thetree_growth="level"restriction — multiclass DART now also works withtree_growth="leaf"+max_leaves. - Piecewise-linear leaves via
leaf_model="linear"(closed-form ridge solve, faster convergence on linear-trend data) - Factor-neutral boosting via
neutralization+ fit-timefactor_exposures(pre_target,per_round_gradient,split_penalty) with optionalfactor_exposure_transform - LightGBM-compatible feature interaction constraints via
interaction_constraints=[[...]](up to 64 groups, level-wise and leaf-wise enforcement) - Neutralized warm-start /
init_modelcontinuation with matching-exposures contract - Per-round training diagnostics via
diagnostics_per_round_(gradient stats, sampling counts,neutralization_effectiveness)
Inference and Explanations
- Zero-copy numpy prediction from native artifacts
- TreeSHAP explanations via
shap_values(...)(polynomial-time, no feature limit, also supportsleaf_model="linear"as a best-effort interventional decomposition) - Pairwise SHAP interaction values (v0.11.0+) via
GBMRegressor.shap_interaction_values(X)— Lundberg et al. (2020) Algorithm 2 in polynomial timeO(T·L·D²·M). Row marginal recovers per-feature SHAP; full sum reconstructs the prediction. - Global feature importance via
feature_importances(...) - Artifact-backed prediction via
predict_from_artifact(...)
Validation Helpers
purged_time_series_splits(...)-- leakage-aware time-series cross-validationpurged_panel_splits(...)-- panel-data cross-validation
Metrics
- Regression:
rmse,mae,r2_score - GLM deviance (v0.11.0+):
poisson_deviance,gamma_deviance,tweedie_deviance - Classification:
accuracy,log_loss - Ranking:
ndcg - Finance:
pearson_correlation,rank_ic,hit_rate,icir
Benchmark Snapshot
The benchmark suite compares AlloyGBM against XGBoost, LightGBM, and CatBoost across regression, classification, and ranking tasks.
Regression:
- AlloyGBM is strongest on
panel_time_series - AlloyGBM is strong on
dow_jones_financial - AlloyGBM is competitive on
dense_numeric, trails oncalifornia_housingandbike_sharing
Classification:
- AlloyGBM is competitive with established libraries on
breast_cancerandsynthetic_classification
Ranking:
- AlloyGBM competes on
synthetic_rankingusing its native LambdaMART implementation
Benchmark tooling and methodology live in benchmarks/README.md.
Current Limitations
- CPU-only runtime (GPU backend is architecturally planned but not implemented)
MultiLabelGBMRanker(multi_label_mode="joint")supports built-insquared_error/queryrmse/rank:*objectives, plus (as of v0.12.8) thepoisson/gamma/tweedie/quantileregression objectives. v0.10.2 added leaf-wise growth +max_leaves,interaction_constraints,min_split_gain,row_subsample, andcol_subsample. v0.10.3 added native-categorical Python wiring, joint GOSS, joint DART, and joint warm-start. v0.10.4 added joint MorphBoost (training_mode="morph"+ the full morph kwargs). v0.10.5 added joint DRO leaves (leaf_solver="dro"+dro_radius/dro_metric). v0.10.6 added joint factor neutralization (all three modes:pre_target,per_round_gradient,split_penalty) — the joint trainer now has full feature parity with the single-output path.leaf_solver="dro"is a robust scalar leaf update, not a full raw-distribution Wasserstein DRO guarantee- Quantile regression objective (
"quantile") is supported with linear leaves, DART, and MorphBoost, and (as of v0.12.8) onGBMRankerandMultiLabelGBMRanker(both modes). It is still rejected for classification and multiclass training.
See docs/limitations.md for the full list.
Documentation
- Docs index: docs/README.md
- Hosted Sphinx docs: alloygbm.readthedocs.io
- Runnable examples: examples/ (8 end-to-end scripts)
- Benchmark guide: benchmarks/README.md
- Current roadmap: docs/roadmap/current.md
- Current limitations: docs/limitations.md
- Archive: docs/archive/README.md
Contributing
- Contributing guide (dev setup, coding standards, test commands)
- Security policy (private vulnerability reporting)
- Release operating manual
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file alloygbm-0.12.10.tar.gz.
File metadata
- Download URL: alloygbm-0.12.10.tar.gz
- Upload date:
- Size: 519.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c4b120ac9314887d3faaa5aefcfa14b1d99a789d90b45eb5cce0cfb2cbb20a22
|
|
| MD5 |
3490e0196771d8b3ee586e7a146f7185
|
|
| BLAKE2b-256 |
77eac7ccce9f1b7983256a87908c8f5c7e3d39ad6d8fefb92a3674f23fc0f4c1
|
Provenance
The following attestation bundles were made for alloygbm-0.12.10.tar.gz:
Publisher:
publish.yml on LGA-Personal/AlloyGBM
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
alloygbm-0.12.10.tar.gz -
Subject digest:
c4b120ac9314887d3faaa5aefcfa14b1d99a789d90b45eb5cce0cfb2cbb20a22 - Sigstore transparency entry: 2047858174
- Sigstore integration time:
-
Permalink:
LGA-Personal/AlloyGBM@cdc5bf07ef678f4bce8384d5eccf72d85548d931 -
Branch / Tag:
refs/tags/v0.12.10 - Owner: https://github.com/LGA-Personal
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cdc5bf07ef678f4bce8384d5eccf72d85548d931 -
Trigger Event:
release
-
Statement type:
File details
Details for the file alloygbm-0.12.10-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: alloygbm-0.12.10-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 1.4 MB
- Tags: CPython 3.11+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0234d4d62e63569639fab1b8ffe98dd6c9ca2cda05e3b963d881d242607f71dc
|
|
| MD5 |
04abbd3dc759b8afc7f9bab3b8083186
|
|
| BLAKE2b-256 |
f09da7b757fe2bae9ce925d25e3e7374ba4e0b396858b9f9464de594402a9571
|
Provenance
The following attestation bundles were made for alloygbm-0.12.10-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish.yml on LGA-Personal/AlloyGBM
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
alloygbm-0.12.10-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
0234d4d62e63569639fab1b8ffe98dd6c9ca2cda05e3b963d881d242607f71dc - Sigstore transparency entry: 2047858207
- Sigstore integration time:
-
Permalink:
LGA-Personal/AlloyGBM@cdc5bf07ef678f4bce8384d5eccf72d85548d931 -
Branch / Tag:
refs/tags/v0.12.10 - Owner: https://github.com/LGA-Personal
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cdc5bf07ef678f4bce8384d5eccf72d85548d931 -
Trigger Event:
release
-
Statement type:
File details
Details for the file alloygbm-0.12.10-cp311-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: alloygbm-0.12.10-cp311-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.2 MB
- Tags: CPython 3.11+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0131a3a1f0ea605acb407bc9cf4585a42787fbdfe8ce0d458439ee0056f40ff
|
|
| MD5 |
ec8dfb6563c0830d4ac6b9ad0b6191a9
|
|
| BLAKE2b-256 |
2b60a29c0adff759293b355cb17098369b2dfd31c3a7808fa326249fec11d7be
|
Provenance
The following attestation bundles were made for alloygbm-0.12.10-cp311-abi3-macosx_11_0_arm64.whl:
Publisher:
publish.yml on LGA-Personal/AlloyGBM
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
alloygbm-0.12.10-cp311-abi3-macosx_11_0_arm64.whl -
Subject digest:
b0131a3a1f0ea605acb407bc9cf4585a42787fbdfe8ce0d458439ee0056f40ff - Sigstore transparency entry: 2047858190
- Sigstore integration time:
-
Permalink:
LGA-Personal/AlloyGBM@cdc5bf07ef678f4bce8384d5eccf72d85548d931 -
Branch / Tag:
refs/tags/v0.12.10 - Owner: https://github.com/LGA-Personal
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cdc5bf07ef678f4bce8384d5eccf72d85548d931 -
Trigger Event:
release
-
Statement type: