treecf 0.3.0
Added
- Native categorical splits. Models trained with native categorical support now parse
exactly into set-membership IR nodes: LightGBM (categorical_feature), XGBoost
(enable_categorical), scikit-learnHistGradientBoosting(categorical_features,
including string categories viacategories=), and CatBoost (cat_features; one-hot and
single-feature-statistic splits, hashing reproduced bit-exactly). CatBoost models built with
categorical feature combinations raiseParserErrornaming themax_ctr_complexity=1
retraining recipe; the newParserErrortype covers "recognized but unparseable as given". Explainer(categories=...). Display names (and, where useful, declared cardinalities
beyond training) for categorical features; required for CatBoost with native categorical
features and for HistGradientBoosting trained on string categories, optional elsewhere.- Category blocks. Every backend searches categorical features over routing-equivalence
classes of codes, so search cost scales with how finely the ensemble partitions the feature,
not its cardinality. Categorical distance is flat: any change of code costs one weighted
unit. AllowedCategoriesconstraint. Whitelist a categorical feature's codes by display name
or raw code; order- and arithmetic-shaped constraints (Range,Monotone,Linear,
Equals,Implies,OneHot) are rejected on categorical features at construction.- Categorical exact search and regions.
backend="exact"proves optimality and certified
infeasibility over the block grid;region=Truecertifies category sets per categorical
feature (RecourseRegion.feature_categories/.category_names/.cat_sets), stored in
certificates as schema version 2. Schema version 1 certificates still verify, pinned by a
committed golden file. - Presolve. The exact backend filters each feature's candidate states by reachable score
and plausibility brackets before branching;solver_statsgainspresolve_removedand
presolve_certified, and an emptied domain certifies infeasibility with zero nodes
expanded. Results are bit-identical with presolve on; only node counts drop. - Visualization.
plot_region(the certified box, with per-bound cap markers and
categorical tiles) andplot_recourse_burden/recourse_burden_table(feasible share and
cost distribution by segment, kept side by side). - Docs. Reader-oriented navigation (workflow guides, grouped concepts, split API pages,
benchmarks and changelog pages); every fenced snippet executed in CI against a committed
docs model; a structure test pins that no published URL disappears and every plot function
ships a committed figure. SECURITY.md,CONTRIBUTING.md,scripts/bump_version.py(with a version-consistency
test), and#![forbid(unsafe_code)]in the Rust core.
Changed
-
Exact-search performance. Presolve, a feature-to-trees index, and per-tree bracket
caching in region growth. Measured before/after (same machine, same seeds, medians):Scenario (exact backend, warm start, 5 s / 2M-node budgets, 10 seeds, 4-core x86_64) 0.2.4 median 0.3.0 median 30 trees / depth 4 / 8 features — every solve proved optimal 0.292 s 0.295 s 60 trees / depth 5 / 12 features — budget-capped, best-found 5.014 s 5.012 s 300 trees / depth 6 / 50 features — budget-capped, best-found 5.208 s 5.133 s No legacy case regresses (the largest change is +1.0% on the small case, within run
noise); on the large budget-capped case the search now expands 308,822 nodes in the same
budget where 0.2.4 expanded 205,755. New certification measurements (60 s budget,
3 seeds): the 200-tree / depth-5 / 12-feature reference case does not certify within
60 s on the 4-core benchmark machine; the native-categorical suite (4 numeric levers plus
cardinality-3/8/15 categoricals, LightGBM) certifies in 0.022 s median even at 200 trees.
Full tables: the docs benchmarks page, generated from the same measured JSON.
Invariants
- Numeric-model results are byte-identical to the previous release: fingerprints, solves,
regions, and stored fixtures are unchanged, pinned by a dedicated invariance suite. - No genetic or parity fixture was regenerated; exact fixtures were regenerated only under an
equality guard asserting identical plans, distances, and proofs. - The Python and Rust engines remain byte-identical on every solve, domain, and region,
including the new categorical paths.