contree/lib.rs
1//! Optimal decision trees over continuous features.
2//!
3//! Two searches are provided:
4//!
5//! * [`ConTree`](algorithms::ConTree), an exact branch-and-bound search with a
6//! cache of subproblems and a specialised depth-2 solver. Candidate
7//! thresholds lie between consecutive observed values of each feature, and
8//! whole intervals of thresholds are pruned at once (Brită, van der Linden
9//! and Demirović, AAAI 2025).
10//! * [`ConTreeLds`](algorithms::ConTreeLds), an anytime version that runs the
11//! same search in passes of growing limited discrepancy budget, so a good
12//! tree is available early (Kiossou, Schaus and Nijssen, *Anytime Optimal
13//! Decision Tree Learning with Continuous Features*, ECML PKDD 2026).
14//!
15//! Labels must be the integers `0..num_labels`, and a split sends an instance
16//! left when `x[feature] <= threshold`, as in scikit-learn.
17//!
18//! ```
19//! use contree::algorithms::ConTree;
20//! use contree::common::{PointSelector, SearchConfig};
21//! use contree::data::Dataset;
22//!
23//! // Four instances, one feature, row-major.
24//! let values = [0.1, 0.4, 0.6, 0.9];
25//! let labels = [0, 0, 1, 1];
26//! let dataset = Dataset::from_rows(&values, &labels, 1).unwrap();
27//!
28//! let config = SearchConfig::new(1, 2, 60.0, 0, usize::MAX, false, true, PointSelector::Mid);
29//! let outcome = ConTree::with_config(config).fit(&dataset).unwrap();
30//! assert_eq!(outcome.error(), 0);
31//! assert_eq!(outcome.tree.predict_one(&[0.7]), Ok(1));
32//! ```
33
34// Library code returns text to its caller instead of printing it.
35#![cfg_attr(not(test), warn(clippy::print_stdout, clippy::print_stderr))]
36
37pub mod algorithms;
38mod bitsets;
39mod caching;
40pub mod common;
41pub mod data;
42pub mod reader;
43pub mod tree;
44
45#[cfg(test)]
46mod tests {
47 use std::path::PathBuf;
48
49 /// Absolute path to a file in `crates/contree/tests/fixtures/`.
50 pub fn fixture(name: &str) -> PathBuf {
51 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
52 .join("tests/fixtures")
53 .join(name)
54 }
55}