1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
//! # hessboost
//!
//! Fast, deterministic gradient boosting in Rust (with Python bindings).
//! hessboost provides multi-core tree building with runtime-detected NEON and AVX2
//! SIMD, strict parameter validation, reproducible models on any thread count, and
//! stable model storage. It supports modern extensions like conformal prediction,
//! explainable boosting machines (EBMs), distributional modeling, and tree-based
//! diffusion, alongside bidirectional XGBoost JSON/UBJSON model interchange.
//!
//! ## Quick start
//!
//! Build a [`DMatrix`], set [`TrainingParams`] with its builder, train with
//! [`train`] (or [`Trainer`] for eval sets, early stopping, custom hooks,
//! and continued training), then
//! [`predict`](model::BoostedModel::predict):
//!
//! ```
//! use hessboost::prelude::*;
//!
//! # fn main() -> Result<()> {
//! // 6 rows × 2 features, row-major, plus a label per row.
//! let x = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.5, 0.5, 0.2, 0.9];
//! let y = [0.0, 1.0, 1.0, 0.0, 0.5, 0.7];
//! let dtrain = DMatrix::from_dense(&x, 6, 2)?.with_labels(&y)?;
//!
//! let params = TrainingParams::builder()
//! .objective(Objective::SquaredError(RegLoss::default()))
//! .tree_method(TreeMethod::Hist)
//! .max_depth(3)
//! .eta(0.1)
//! .build()?;
//!
//! let model = train(¶ms, &dtrain, 50)?;
//! let preds = model.predict(&dtrain, Iterations::Best)?;
//! assert_eq!((preds.n_rows(), preds.width()), (6, 1)); // `[row][output]`
//!
//! model.save("model.bin", ModelFormat::Binary)?; // native format
//! # std::fs::remove_file("model.bin").ok();
//! # Ok(())
//! # }
//! ```
//!
//! [`prelude`] holds only this workflow's items (including
//! [`TreeMethod`](config::TreeMethod) and [`Objective`](objective::Objective));
//! everything else is imported from its module.
//!
//! ## Modules
//!
//! - [`config`]: [`TrainingParams`], builder, parameter enums.
//! - [`data`]: [`DMatrix`], [`MetaInfo`](data::MetaInfo), feature types,
//! CSV/libsvm loaders, [`data::target_stats`].
//! - [`training`]: [`train`], [`Trainer`], [`cv`](training::cv),
//! [`training::budget`], [`training::online`].
//! - [`model`]: [`BoostedModel`] (prediction, SHAP, importance, slicing,
//! native and XGBoost JSON/UBJSON, LightGBM text import);
//! [`model::compact`], [`model::uncertainty`].
//! - [`objective`]: [`Objective`](objective::Objective) and its parameter
//! types, the `Loss` trait, `CustomLoss`, [`objective::distributional`]
//! (`dist:*` objectives).
//! - [`metric`]: [`EvalMetric`](metric::EvalMetric) (the built-in metrics),
//! the `Metric` trait, `CustomMetric`.
//! - [`conformal`]: split-conformal and conformalized-quantile intervals.
//! - [`inference`]: Boulevard boosting's confidence and prediction intervals
//! for `f(x)`, and a Boulevard EBM's shape-function bands.
//! - [`ebm`]: explainable boosting machines' terms and shape functions.
//! - [`diffusion`]: conditional diffusion and flow matching with GBDT score
//! models, sampling a nonparametric `p(y | x)`.
//! - [`tree`]: [`RegTree`](tree::RegTree) and nodes, for model inspection.
//! - [`error`]: `HessboostError` and `Result`.
//!
//! ## What's here
//!
//! - **Boosters:** [`BoosterKind::GbTree`](config::BoosterKind::GbTree),
//! [`BoosterKind::Dart`](config::BoosterKind::Dart),
//! [`BoosterKind::GbLinear`](config::BoosterKind::GbLinear), boosted random
//! forests (`num_parallel_tree`),
//! [`BoosterKind::Boulevard`](config::BoosterKind::Boulevard), and
//! [`BoosterKind::Ebm`](config::BoosterKind::Ebm).
//! - **Lifecycle:** continued training and `process_type=update` refresh
//! ([`Trainer::init_model`](training::Trainer::init_model)), a per-round
//! hook for progress, custom stopping, and cancellation
//! ([`Trainer::on_round`](training::Trainer::on_round)), slicing
//! ([`BoostedModel::slice`](model::BoostedModel::slice)), `iteration_range`
//! prediction as Rust ranges (every prediction method's
//! [`Iterations`](model::Iterations) argument).
//! - **Tree methods:** `exact`, `hist`, `approx`; `depthwise`/`lossguide`
//! growth; uniform or `gradient_based` row sampling; column sampling with
//! optional per-feature weights
//! ([`DMatrix::with_feature_weights`](data::DMatrix::with_feature_weights)).
//! - **Objectives** ([`Objective`](objective::Objective), each with its
//! parameters): regression (squared, squared-log, pseudo-Huber, smoothed
//! absolute, quantile/expectile lists), binary (logistic, logitraw, hinge)
//! and multiclass, counts, LambdaMART and XE-NDCG ranking, survival (`survival:cox`,
//! `survival:aft` on censored bounds), plus custom losses
//! ([`Objective::Custom`](objective::Objective::Custom), e.g. a
//! [`CustomLoss`](objective::CustomLoss)).
//! - **Multi-output:** label matrices
//! ([`DMatrix::with_label_matrix`](data::DMatrix::with_label_matrix)), one
//! tree per output or vector-leaf trees
//! ([`MultiStrategy::MultiOutputTree`](config::MultiStrategy::MultiOutputTree)).
//! - **Metrics** ([`EvalMetric`](metric::EvalMetric), each with its own
//! parameters): rmse, rmsle, mae, mape, mphe, logloss, error, auc, aucpr,
//! mlogloss, merror, poisson/gamma/tweedie-nloglik, ndcg, map, pre,
//! quantile, expectile, cox/aft-nloglik, interval-regression-accuracy, plus
//! a custom hook ([`Trainer::custom_metric`](training::Trainer::custom_metric),
//! reported after built-in metrics). XGBoost-compatible parameter dictionaries parse through
//! [`TrainingParams::from_xgboost`](config::TrainingParams::from_xgboost):
//! `@k` ranking cutoffs and `@rho` on tweedie-nloglik, other suffixes
//! refused.
//! - **Modeling:** monotone and interaction constraints, native categorical
//! splits, early stopping, feature importance, QuadratureTreeSHAP values
//! and interactions
//! ([`predict_contribs`](model::BoostedModel::predict_contribs) /
//! [`predict_interactions`](model::BoostedModel::predict_interactions)).
//! - **I/O:** libsvm/CSV loaders, native binary + JSON, XGBoost JSON and
//! UBJSON import/export ([XGBoost interchange](model#xgboost-interchange)),
//! LightGBM 4.x text model import
//! ([`ModelFormat::LightgbmText`](model::ModelFormat::LightgbmText); see
//! [LightGBM import](model#lightgbm-import)), all through one
//! [`ModelFormat`](model::ModelFormat) with byte-level
//! [`detect`](model::ModelFormat::detect)ion.
//! - **Validation:** cross-validation ([`cv`](training::cv)), custom,
//! forward-chaining (time-ordered, purged by a row gap), or purged forward
//! (timestamped rows, purged by each label window,
//! [`Fold::purged_forward`](training::Fold::purged_forward))
//! [`Fold`](training::Fold)s with fold-mean early stopping
//! ([`CrossValidation`](training::CrossValidation)).
//! - **Advanced & experimental methods (opt-in):**
//! - split-conformal and conformalized-quantile intervals with
//! finite-sample marginal coverage ([`conformal`]);
//! - Boulevard boosting (Zhou & Hooker, JMLR 2022) and its dropout
//! (BRAT-D) and parallel (BRAT-P) variants (Fang, Tan & Hooker, NeurIPS
//! 2025) with CLT-based confidence intervals for `f(x)`, prediction and
//! reproduction intervals, an
//! honest leaf refit, and exact or Nyström variance
//! ([`BoosterKind::Boulevard`](config::BoosterKind::Boulevard),
//! [`inference`]);
//! - explainable boosting machines (GA²M: cyclic per-feature boosting,
//! early-stopped outer bags, FAST pair terms, numerical and categorical
//! terms; Lou et al., KDD 2012/2013, InterpretML)
//! with per-term shape functions, and their Boulevard variant (Fang, Tan,
//! Pipping & Hooker, AISTATS 2026) with confidence bands on every shape
//! ([`BoosterKind::Ebm`](config::BoosterKind::Ebm), [`ebm`],
//! [`EbmInference`](inference::EbmInference));
//! - CatBoost-style ordered target statistics ([`data::target_stats`]);
//! - LightGBM options `extra_trees`, `path_smooth`, `linear_tree` leaves
//! ([`TrainingParams::extra_trees`](config::TrainingParams::extra_trees),
//! [`path_smooth`](config::TrainingParams::path_smooth),
//! [`linear_tree`](config::TrainingParams::linear_tree),
//! [`LinearLeaves`](tree::LinearLeaves));
//! - LightGBM class-balanced bagging for binary classification
//! ([`BalancedBagging`](config::BalancedBagging): `pos_bagging_fraction`,
//! `neg_bagging_fraction`), in place of `subsample`;
//! - LightGBM XE-NDCG ranking
//! ([`Objective::RankXendcg`](objective::Objective::RankXendcg); its keyed
//! per-round draws differ from LightGBM's random stream) and query-level
//! bagging ([`QueryBagging`](config::QueryBagging), `bagging_by_query`);
//! - CatBoost-style symmetric trees
//! ([`GrowPolicy::Symmetric`](config::GrowPolicy::Symmetric)), routed by
//! bit pattern in batch prediction;
//! - *Boosted Trees on a Diet* reuse penalties
//! (`toad_penalty_feature`, `toad_penalty_threshold`) and a bit-packed
//! layout with bit-identical margins ([`model::compact`]);
//! - LightGBM-style quantized gradients
//! ([`QuantizedGrad`](config::QuantizedGrad), `use_quantized_grad`);
//! - PerpetualBooster-style budget training: one `budget` instead of
//! `eta`/depth/rounds ([`training::budget`]);
//! - in-place row addition and deletion (incremental learning and machine
//! unlearning) for trained hist models, exact or approximate
//! ([`training::online`]);
//! - distributional boosting (NGBoost / XGBoostLSS style): `dist:normal`,
//! `dist:lognormal`, `dist:gamma`, `dist:poisson`, `dist:negbinomial`
//! per-row distributions
//! ([`predict_distribution`](model::BoostedModel::predict_distribution),
//! [`objective::distributional`]), scored by `nll` / `crps`;
//! - CatBoost's Stochastic Gradient Langevin Boosting and model shrinkage
//! ([`langevin`](config::TrainingParams::langevin),
//! [`model_shrink`](config::TrainingParams::model_shrink),
//! [`posterior_sampling`](config::TrainingParams::posterior_sampling))
//! with virtual ensembles: knowledge, data, and total uncertainty from
//! one model's exactly rebuilt truncations
//! ([`predict_uncertainty`](model::BoostedModel::predict_uncertainty),
//! [`model::uncertainty`]);
//! - nonparametric `p(y | x)` by tree-based conditional diffusion
//! (Treeffuser) and flow matching (DiffGBM) for scalar or vector
//! labels, sampled deterministically ([`diffusion`]);
//! - ForestFlow / ForestDiffusion tabular generation and imputation
//! ([`diffusion::forest`]);
//! - native Metal on macOS 10.15+ (`metal` feature): bit-identical GPU
//! prediction ([`to_gpu`](model::BoostedModel::to_gpu), ~2.5x faster at
//! scale) and bit-identical GPU histograms
//! ([`device`](config::TrainingParams::device) = `metal`; exact integer
//! sums, CPU fallback outside their exact domain). Documented only in
//! macOS builds with the feature (`cargo doc --features metal`);
//! elsewhere [`backend::metal`] is a stub.
//!
//! `examples/` has one program per topic (`train_regression`,
//! `binary_classification`, `multiclass`, `ranking`, `rank_xendcg`, `shap`,
//! `model_io`, `custom_objective`, `constraints`, `conformal`,
//! `boulevard_inference`, `ebm`, `compact_model`, `distributional`,
//! `virtual_ensembles`, `tree_diffusion`, `forest_flow`, `budget`,
//! `balanced_bagging`, `online_update`, `ordered_target_stats`, `pfn_boost`,
//! `metal` with `--features metal` on macOS). Run one with
//! `cargo run --release --example binary_classification`.
//!
//! ## Compatibility notes
//!
//! While hessboost is a standalone library, it offers extensive compatibility with
//! XGBoost configurations and models:
//! [`TrainingParams::from_xgboost`](config::TrainingParams::from_xgboost)
//! reads XGBoost `params` dictionaries (keys, aliases, and value spellings), and
//! unsupported settings are refused. Deterministic configurations reproduce XGBoost
//! predictions within `1e-4` (quantile cuts bit for bit), and imported XGBoost models
//! predict and explain identically. RNG-driven options (subsampling,
//! forests, DART) match in quality only — the random streams differ.
//! ### Not implemented
//!
//! - Distributed and external-memory training; GPU training outside macOS.
//! - XGBoost options available at one setting only (so they are not
//! [`TrainingParams`] fields; `from_xgboost` accepts exactly that
//! setting): gblinear uses `updater = coord_descent`
//! with `feature_selector = cyclic`; LambdaMART uses
//! `lambdarank_pair_method = topk` (no `lambdarank_unbiased` or
//! `ndcg_exp_gain`); DART has no `sample_type` or `normalize_type` (it
//! samples uniformly and normalizes by `tree`); categorical splits use
//! XGBoost's defaults
//! `max_cat_to_onehot = 4` and `max_cat_threshold = 64`.
//! - The metrics `gamma-deviance`, `error@t` (XGBoost's classification
//! threshold suffix), and the `-` variants of the ranking metrics
//! (`ndcg-`, `ndcg@k-`, `map-`, `map@k-`); these names are refused.
//! - XGBoost import and export of gblinear models.
//! - `scale_pos_weight = 0`: XGBoost's bound is `>= 0`; hessboost's
//! [`RegLoss::new`](objective::RegLoss::new) needs a positive weight, so
//! configurations and XGBoost files with `0` are refused.
//!
//! [`DMatrix`]: data::DMatrix
//! [`TrainingParams`]: config::TrainingParams
//! [`BoostedModel`]: model::BoostedModel
//! [`train`]: training::train
//! [`Trainer`]: training::Trainer
/// `1e-6` in `f64` arithmetic, where the crate compares against XGBoost's
/// `kRtEps` in double precision (the `f64` literal, not [`K_RT_EPS_F32`]
/// widened).
pub const K_RT_EPS: f64 = 1e-6;
/// XGBoost's `kRtEps` (`1e-6f`): the minimum gain improvement a split must
/// beat, and the floor of sampling weights and near-zero sums.
pub const K_RT_EPS_F32: f32 = 1e-6;
/// The train-and-predict workflow in one import: `use hessboost::prelude::*;`.
///
/// Holds the data container, the parameters, the training entry points, the
/// model, the error types, and the types their everyday methods take:
/// [`TreeMethod`](config::TreeMethod) (for
/// [`TrainingParamsBuilder::tree_method`](config::TrainingParamsBuilder::tree_method)),
/// [`ImportanceType`](model::ImportanceType) (for
/// [`BoostedModel::feature_importance`](model::BoostedModel::feature_importance)),
/// [`Objective`](objective::Objective) (for
/// [`TrainingParamsBuilder::objective`](config::TrainingParamsBuilder::objective))
/// with [`RegLoss`](objective::RegLoss) (the parameter of its default
/// `reg:squarederror`), and [`EvalMetric`](metric::EvalMetric) (for
/// [`TrainingParamsBuilder::eval_metric`](config::TrainingParamsBuilder::eval_metric)).
/// Everything else (the other parameter enums, the other objectives' and
/// metrics' parameters, conformal intervals, ...) is imported from its module.
/// Implementation details the crate's own benchmarks and parity tests
/// drive directly (histogram construction, tree growth, quantile cuts). Not
/// part of the public API: hidden from the docs and changed without notice.