contree/common/
outcome.rs1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6use crate::common::{Cost, Statistics};
7use crate::data::DatasetError;
8use crate::tree::{Tree, TreeError};
9
10#[derive(Clone, Debug, PartialEq)]
12pub enum SearchError {
13 EmptyDataset,
15 NoFeatures,
17 UnpreparedDataset,
25 InvalidParameter { name: &'static str, reason: String },
27 Data(DatasetError),
29 Tree(TreeError),
32 InvalidLeafCost { cost: Cost },
34}
35
36impl fmt::Display for SearchError {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 match self {
39 SearchError::EmptyDataset => write!(f, "the dataset has no instances"),
40 SearchError::NoFeatures => write!(f, "the dataset has no features"),
41 SearchError::UnpreparedDataset => write!(
42 f,
43 "the dataset has not been sorted and indexed; build it with \
44 Dataset::from_rows or DataReader"
45 ),
46 SearchError::InvalidParameter { name, reason } => {
47 write!(f, "invalid {name}: {reason}")
48 }
49 SearchError::Data(err) => write!(f, "{err}"),
50 SearchError::Tree(err) => write!(f, "the search produced an invalid tree: {err}"),
51 SearchError::InvalidLeafCost { cost } => write!(
52 f,
53 "the loss returned the leaf cost {cost}; costs must be non-negative numbers"
54 ),
55 }
56 }
57}
58
59impl std::error::Error for SearchError {}
60
61impl From<DatasetError> for SearchError {
62 fn from(err: DatasetError) -> Self {
63 SearchError::Data(err)
64 }
65}
66
67impl From<TreeError> for SearchError {
68 fn from(err: TreeError) -> Self {
69 SearchError::Tree(err)
70 }
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
78#[serde(rename_all = "snake_case")]
79pub enum SearchStatus {
80 Optimal,
82 TimeLimit,
84 BudgetExhausted,
86 ErrorBoundReached,
89 NotProven,
92}
93
94impl SearchStatus {
95 pub fn is_optimal(self) -> bool {
97 matches!(self, SearchStatus::Optimal)
98 }
99}
100
101impl fmt::Display for SearchStatus {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 f.write_str(match self {
104 SearchStatus::Optimal => "optimal",
105 SearchStatus::TimeLimit => "time limit",
106 SearchStatus::BudgetExhausted => "budget exhausted",
107 SearchStatus::ErrorBoundReached => "error bound reached",
108 SearchStatus::NotProven => "not proven",
109 })
110 }
111}
112
113#[derive(Clone, Debug)]
115pub struct FitOutcome {
116 pub tree: Tree,
118 pub statistics: Statistics,
120 pub status: SearchStatus,
122}
123
124impl FitOutcome {
125 pub fn error(&self) -> Cost {
127 self.statistics.error
128 }
129}