Skip to main content

dtrees_rs/algorithms/optimal/rules/
purity.rs

1use crate::algorithms::optimal::rules::core::Reason;
2use crate::algorithms::optimal::rules::{Rule, RuleContext, RuleResult, RuleState};
3
4/// Stops at nodes that are already pure enough.
5///
6/// The purity of a node is the fraction of its instances that it classifies
7/// correctly. Nodes at or above the threshold are not split in this pass;
8/// relaxing the rule raises the threshold by `delta` until it reaches 1.
9pub struct PurityRule {
10    delta: f64,
11    threshold: f64,
12    priority: u8,
13    state: RuleState,
14    relaxable: bool,
15}
16
17impl PurityRule {
18    /// A rule starting at `initial_threshold`, raised by `delta` per pass.
19    pub fn new(initial_threshold: f64, delta: f64) -> Self {
20        Self {
21            delta,
22            threshold: initial_threshold,
23            priority: 90,
24            state: RuleState::Disabled,
25            relaxable: true,
26        }
27    }
28
29    /// Sets the evaluation priority.
30    pub fn with_priority(mut self, priority: u8) -> Self {
31        self.priority = priority;
32        self
33    }
34}
35
36impl Rule for PurityRule {
37    fn evaluate(&self, context: &RuleContext) -> RuleResult {
38        let purity = 1.0 - context.error.min(context.leaf_error) / context.support as f64;
39        if purity >= self.threshold {
40            RuleResult::stop_with_bound(f64::INFINITY, Reason::RuleReason)
41        } else {
42            RuleResult::continue_search()
43        }
44    }
45
46    fn priority(&self) -> u8 {
47        self.priority
48    }
49
50    fn description(&self) -> String {
51        "Purity rule".to_string()
52    }
53
54    fn state(&self) -> RuleState {
55        self.state
56    }
57
58    fn activate(&mut self) {
59        self.state = RuleState::Active
60    }
61
62    fn deactivate(&mut self) {
63        self.state = RuleState::Disabled
64    }
65
66    fn relax(&mut self) {
67        if !self.is_active() {
68            return;
69        }
70
71        if self.is_relaxable() && self.threshold >= 1.0 {
72            self.deactivate();
73            return;
74        }
75        self.threshold += self.delta;
76        if self.threshold >= 1.0 {
77            self.threshold = 1.0
78        }
79    }
80
81    fn is_relaxable(&self) -> bool {
82        self.relaxable
83    }
84
85    fn as_any(&self) -> &dyn std::any::Any {
86        self
87    }
88
89    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
90        self
91    }
92}