Skip to main content

radiate_gp/collections/trees/
tree.rs

1use crate::{TreeIterator, collections::TreeNode};
2#[cfg(feature = "serde")]
3use serde::{Deserialize, Serialize};
4use std::{fmt::Debug, hash::Hash};
5
6/// A tree structure that represents a hierarchical collection of nodes.
7///
8/// The `Tree` struct is a fundamental data structure in Radiate's genetic programming system.
9/// It provides a way to represent and manipulate tree-based expressions, where each node
10/// can have zero or more child nodes. The tree is rooted, meaning it has a single root node
11/// from which all other nodes descend.
12///
13/// # Type Parameters
14/// * `T` - The type of value stored in each node. This type must implement `Clone`, `PartialEq`,
15///         and other traits required by the genetic programming operations.
16///
17/// # Fields
18/// * `root` - An optional `TreeNode<T>` that serves as the root of the tree. When `None`,
19///            the tree is considered empty.
20///
21/// # Examples
22/// ```
23/// use radiate_gp::{Tree, TreeNode, Op, Eval};
24///
25/// // Create a simple tree representing the expression (1 + 2) * 3
26/// let tree = Tree::new(
27///     TreeNode::new(Op::mul())
28///         .attach(
29///             TreeNode::new(Op::add())
30///                 .attach(TreeNode::new(Op::constant(1.0)))
31///                 .attach(TreeNode::new(Op::constant(2.0)))
32///         )
33///         .attach(TreeNode::new(Op::constant(3.0)))
34/// );
35///
36/// // Evaluate the tree
37/// let result = tree.eval(&[]); // Evaluates to 9.0
38/// assert_eq!(result, 9.0);
39/// ```
40///
41/// # Tree Creation
42/// The struct provides several ways to create trees:
43/// * `new()` - Creates a tree with a given root node
44/// * `with_depth()` - Creates a tree of specified depth using nodes from a `NodeStore`
45/// * `default()` - Creates an empty tree
46///
47/// # Tree Operations
48/// The struct provides methods for tree manipulation and traversal:
49/// * `root()` - Gets a reference to the root node
50/// * `root_mut()` - Gets a mutable reference to the root node
51/// * `take_root()` - Takes ownership of the root node
52/// * `size()` - Returns the total number of nodes in the tree
53/// * `height()` - Returns the height of the tree
54///
55/// # Tree Traversal
56/// The struct implements the `TreeIterator` trait, providing three traversal methods:
57/// * `iter_pre_order()` - Traverses the tree in pre-order (root, then children)
58/// * `iter_post_order()` - Traverses the tree in post-order (children, then root)
59/// * `iter_breadth_first()` - Traverses the tree level by level
60///
61/// # Tree Building
62/// The struct provides a builder pattern for creating trees of specific depths:
63/// ```rust
64/// use radiate_gp::{Tree, NodeType, Op};
65///
66/// let store = vec![
67///     (NodeType::Vertex, vec![Op::add(), Op::sub(), Op::mul()]),
68///     (NodeType::Leaf, vec![Op::constant(1.0), Op::constant(2.0)]),
69/// ];
70///
71/// // Create a tree of depth 3
72/// let tree = Tree::with_depth(3, store);
73/// assert_eq!(tree.height(), 3);
74/// ```
75///
76/// # Tree Evaluation
77/// When `T` implements the `Eval` trait, the tree can be evaluated with input data:
78/// ```rust
79/// use radiate_gp::{Tree, TreeNode, Op, Eval};
80///
81/// let tree = Tree::new(
82///     TreeNode::new(Op::add())
83///         .attach(TreeNode::new(Op::var(0)))
84///         .attach(TreeNode::new(Op::constant(2.0)))
85/// );
86///
87/// assert_eq!(tree.eval(&[1.0]), 3.0);
88/// assert_eq!(tree.eval(&[2.0]), 4.0);
89/// ```
90///
91/// # Tree Properties
92/// The tree maintains several important properties:
93/// * It is always rooted (has a single root node)
94/// * It is acyclic (no node is its own ancestor)
95/// * Each node can have zero or more children
96/// * The tree's height is the length of the longest path from root to leaf
97/// * The tree's size is the total number of nodes
98///
99/// # Implementation Details
100/// The struct implements several traits:
101/// * `Clone` - Allows cloning of the entire tree structure
102/// * `PartialEq` - Enables equality comparison between trees
103/// * `Default` - Provides a way to create an empty tree
104/// * `Debug` - Provides debug formatting for the tree
105/// * `AsRef<TreeNode<T>>` - Allows treating the tree as a reference to its root node
106/// * `AsMut<TreeNode<T>>` - Allows treating the tree as a mutable reference to its root node
107///
108/// # Genetic Programming
109/// The `Tree` struct is particularly useful in genetic programming as it can represent:
110/// * Mathematical expressions
111/// * Program syntax trees
112/// * Decision trees
113/// * Other hierarchical structures
114#[derive(Clone, PartialEq, Default)]
115#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116pub struct Tree<T> {
117    root: Option<TreeNode<T>>,
118}
119
120impl<T> Tree<T> {
121    pub fn new(root: impl Into<TreeNode<T>>) -> Self {
122        Tree {
123            root: Some(root.into()),
124        }
125    }
126
127    pub fn root(&self) -> Option<&TreeNode<T>> {
128        self.root.as_ref()
129    }
130
131    pub fn root_mut(&mut self) -> Option<&mut TreeNode<T>> {
132        self.root.as_mut()
133    }
134
135    pub fn take_root(self) -> Option<TreeNode<T>> {
136        self.root
137    }
138
139    pub fn size(&self) -> usize {
140        self.root.as_ref().map_or(0, |node| node.size())
141    }
142
143    pub fn height(&self) -> usize {
144        self.root.as_ref().map_or(0, |node| node.height())
145    }
146}
147
148impl<T> AsRef<TreeNode<T>> for Tree<T> {
149    fn as_ref(&self) -> &TreeNode<T> {
150        self.root.as_ref().unwrap()
151    }
152}
153
154impl<T> AsMut<TreeNode<T>> for Tree<T> {
155    fn as_mut(&mut self) -> &mut TreeNode<T> {
156        self.root.as_mut().unwrap()
157    }
158}
159
160impl<T: Hash> Hash for Tree<T> {
161    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
162        self.root.hash(state);
163    }
164}
165
166impl<T: Debug> Debug for Tree<T> {
167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168        write!(f, "Tree {{\n")?;
169        for node in self.iter_breadth_first() {
170            write!(f, "  {:?}\n", node)?;
171        }
172        write!(f, "}}")
173    }
174}
175
176#[cfg(test)]
177mod test {
178
179    use radiate_core::{AlterContext, Lineage, MetricSet};
180
181    use super::*;
182    use crate::{Arity, Node, NodeType, Op, TreeCrossover, TreeIterator};
183
184    #[test]
185    fn test_swap_subtrees() {
186        let mut tree_one = Tree::new(
187            TreeNode::new(Op::add())
188                .attach(TreeNode::new(Op::constant(1.0)))
189                .attach(TreeNode::new(Op::constant(2.0))),
190        );
191
192        let mut tree_two = Tree::new(
193            TreeNode::new(Op::mul())
194                .attach(TreeNode::new(Op::constant(3.0)))
195                .attach(TreeNode::new(Op::constant(4.0))),
196        );
197
198        let copy_one = tree_one.clone();
199        let copy_two = tree_two.clone();
200
201        let mut metrics = MetricSet::default();
202        let mut lineage = Lineage::default();
203
204        let mut ctx = AlterContext::new("TestOperation", &mut metrics, &mut lineage, 0, 1.0);
205
206        TreeCrossover::cross_nodes(tree_one.as_mut(), tree_two.as_mut(), usize::MAX, &mut ctx);
207
208        let new_one = tree_one.clone();
209        let new_two = tree_two.clone();
210
211        // Ensure that subtrees have been swapped
212        assert_ne!(copy_one, new_one);
213        assert_ne!(copy_two, new_two);
214    }
215
216    #[test]
217    fn test_size() {
218        let tree = Tree::new(
219            TreeNode::new(Op::add())
220                .attach(TreeNode::from(Op::constant(1.0)))
221                .attach(TreeNode::from(Op::constant(2.0))),
222        );
223
224        assert_eq!(tree.size(), 3);
225    }
226
227    #[test]
228    fn test_depth() {
229        let store = vec![
230            (NodeType::Vertex, vec![Op::add(), Op::sub(), Op::mul()]),
231            (NodeType::Leaf, vec![Op::constant(1.0), Op::constant(2.0)]),
232        ];
233
234        let tree = Tree::with_depth(5, store);
235        assert_eq!(tree.height(), 5);
236    }
237
238    #[test]
239    fn test_tree_with_mixed_arity() {
240        let store = vec![
241            (
242                NodeType::Vertex,
243                vec![
244                    Op::add(),         // Binary operator
245                    Op::constant(1.0), // Constant
246                    Op::sigmoid(),     // Unary operator - gets treated as arity 2
247                ],
248            ),
249            (NodeType::Leaf, vec![Op::constant(2.0)]),
250        ];
251        let tree = Tree::with_depth(3, store);
252
253        // Verify that nodes have appropriate arity based on their type
254        for node in tree.iter_breadth_first() {
255            match node.value() {
256                Op::Fn(name, arity, _) if *name == "add" || *name == "sub" || *name == "mul" => {
257                    assert_eq!(**arity, 2, "Binary operator should have arity 2")
258                }
259                Op::Const(_, _) => assert_eq!(*node.arity(), 0, "Constant should have arity 0"),
260                Op::Fn(name, arity, _) if *name == "sigmoid" => {
261                    assert!(
262                        vec![0, 1, 2].contains(&**arity),
263                        "Unary operator should have arity 0 or 1 or 2"
264                    )
265                }
266                _ => (), // Other ops can be ignored for this test
267            }
268        }
269    }
270
271    #[test]
272    fn test_tree_with_zero_arity() {
273        let store = vec![
274            (NodeType::Vertex, vec![Op::constant(1.0)]), // Constants have zero arity
275            (NodeType::Leaf, vec![Op::constant(2.0)]),
276        ];
277        let tree = Tree::with_depth(2, store);
278
279        // Verify that each vertex node has no children
280        for node in tree.iter_breadth_first() {
281            println!("Node: {:?}", node);
282
283            assert_eq!(*node.arity(), 0, "Vertex node should have zero arity");
284            assert!(
285                node.children().is_none(),
286                "Vertex node should have no children"
287            );
288        }
289    }
290
291    #[test]
292    fn test_tree_with_exact_arity() {
293        let store = vec![
294            (NodeType::Vertex, vec![Op::add(), Op::sub()]), // Binary operators
295            (NodeType::Leaf, vec![Op::constant(1.0), Op::constant(2.0)]),
296        ];
297        let tree = Tree::with_depth(2, store);
298
299        // Verify that each vertex node has exactly 2 children
300        for node in tree.iter_breadth_first() {
301            if node.node_type() == NodeType::Vertex {
302                assert_eq!(node.arity(), Arity::Exact(2));
303                assert_eq!(node.children().unwrap().len(), 2);
304            }
305        }
306    }
307
308    #[test]
309    fn test_tree_with_only_leaf_nodes() {
310        let store = vec![(NodeType::Leaf, vec![Op::constant(1.0), Op::constant(2.0)])];
311        let tree = Tree::with_depth(3, store);
312        assert!(tree.root().is_none());
313        assert_eq!(tree.size(), 0);
314        assert_eq!(tree.height(), 0);
315    }
316
317    #[test]
318    fn test_tree_with_empty_store() {
319        let empty_store: Vec<(NodeType, Vec<Op<f32>>)> = vec![];
320        let tree = Tree::with_depth(3, empty_store);
321
322        // Root will be a default node since no valid nodes were provided
323        assert!(tree.root().is_none());
324        assert_eq!(tree.size(), 0);
325        assert_eq!(tree.height(), 0);
326    }
327
328    #[test]
329    fn test_tree_debug() {
330        let tree = Tree::new(
331            TreeNode::new(Op::add())
332                .attach(TreeNode::new(Op::constant(1.0)))
333                .attach(TreeNode::new(Op::constant(2.0))),
334        );
335
336        let debug_str = format!("{:?}", tree);
337        assert!(debug_str.contains("Tree {"));
338        assert!(debug_str.contains("add"));
339        assert!(debug_str.contains("C"));
340    }
341
342    #[test]
343    fn test_tree_as_ref_as_mut() {
344        let mut tree = Tree::new(
345            TreeNode::new(Op::add())
346                .attach(TreeNode::new(Op::constant(1.0)))
347                .attach(TreeNode::new(Op::constant(2.0))),
348        );
349
350        // Test AsRef
351        let root_ref: &TreeNode<Op<f32>> = tree.as_ref();
352        assert_eq!(root_ref.value(), &Op::add());
353        assert_eq!(root_ref.children().unwrap().len(), 2);
354
355        // Test AsMut
356        let root_mut: &mut TreeNode<Op<f32>> = tree.as_mut();
357        assert_eq!(root_mut.value(), &Op::add());
358
359        root_mut
360            .children_mut()
361            .unwrap()
362            .push(TreeNode::new(Op::constant(3.0))); // Add a new child
363        assert_eq!(root_mut.children().unwrap().len(), 3); // Now should have 3 children
364        // assert!(!tree.as_ref().is_valid()); // Invalid since we added a child without updating size
365    }
366
367    #[test]
368    fn test_tree_root_operations() {
369        // Test root operations on empty tree
370        let mut empty_tree = Tree::<Op<f32>>::default();
371        assert!(empty_tree.root().is_none());
372        assert!(empty_tree.root_mut().is_none());
373        assert!(empty_tree.take_root().is_none());
374
375        // Test root operations on non-empty tree
376        let tree = Tree::new(
377            TreeNode::new(Op::add())
378                .attach(TreeNode::new(Op::constant(1.0)))
379                .attach(TreeNode::new(Op::constant(2.0))),
380        );
381
382        // Test root()
383        let root = tree.root().unwrap();
384        assert_eq!(root.value(), &Op::add());
385        assert_eq!(root.children().unwrap().len(), 2);
386
387        // Test take_root()
388        let root = tree.take_root().unwrap();
389        assert_eq!(root.value(), &Op::add());
390    }
391
392    #[test]
393    #[cfg(feature = "serde")]
394    fn test_tree_can_serde() {
395        use crate::Eval;
396
397        let store = vec![
398            (
399                NodeType::Vertex,
400                vec![
401                    Op::add(),
402                    Op::sub(),
403                    Op::mul(),
404                    Op::div(),
405                    Op::sigmoid(),
406                    Op::tanh(),
407                ],
408            ),
409            (
410                NodeType::Leaf,
411                vec![Op::constant(1.0), Op::constant(2.0), Op::var(0)],
412            ),
413        ];
414
415        let tree = Tree::with_depth(5, store);
416
417        let eval_before = tree.eval(&[3.0]);
418
419        let serialized = serde_json::to_string(&tree).expect("Failed to serialize tree");
420        let deserialized: Tree<Op<f32>> =
421            serde_json::from_str(&serialized).expect("Failed to deserialize tree");
422
423        let eval_after = deserialized.eval(&[3.0]);
424
425        assert_eq!(
426            eval_before, eval_after,
427            "Tree evaluation should match before and after serialization"
428        );
429        assert_eq!(tree, deserialized);
430    }
431}