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        writeln!(f, "Tree {{")?;
169        for node in self.iter_breadth_first() {
170            writeln!(f, "  {:?}", node)?;
171        }
172        write!(f, "}}")
173    }
174}
175
176#[cfg(test)]
177mod test {
178
179    use super::*;
180    use crate::{Arity, Node, NodeType, Op, TreeCrossover, TreeIterator};
181    use radiate_core::{AlterContext, alter::AlterUpdates};
182
183    #[test]
184    fn test_swap_subtrees() {
185        let mut tree_one = Tree::new(
186            TreeNode::new(Op::add())
187                .attach(TreeNode::new(Op::constant(1.0)))
188                .attach(TreeNode::new(Op::constant(2.0))),
189        );
190
191        let mut tree_two = Tree::new(
192            TreeNode::new(Op::mul())
193                .attach(TreeNode::new(Op::constant(3.0)))
194                .attach(TreeNode::new(Op::constant(4.0))),
195        );
196
197        let copy_one = tree_one.clone();
198        let copy_two = tree_two.clone();
199
200        let mut updates = AlterUpdates::default();
201
202        let mut ctx = AlterContext::new(&mut updates, 0, 1.0, &[]);
203
204        TreeCrossover::cross_nodes(tree_one.as_mut(), tree_two.as_mut(), usize::MAX, &mut ctx);
205
206        let new_one = tree_one.clone();
207        let new_two = tree_two.clone();
208
209        // Ensure that subtrees have been swapped
210        assert_ne!(copy_one, new_one);
211        assert_ne!(copy_two, new_two);
212    }
213
214    #[test]
215    fn test_size() {
216        let tree = Tree::new(
217            TreeNode::new(Op::add())
218                .attach(TreeNode::from(Op::constant(1.0)))
219                .attach(TreeNode::from(Op::constant(2.0))),
220        );
221
222        assert_eq!(tree.size(), 3);
223    }
224
225    #[test]
226    fn test_depth() {
227        let store = vec![
228            (NodeType::Vertex, vec![Op::add(), Op::sub(), Op::mul()]),
229            (NodeType::Leaf, vec![Op::constant(1.0), Op::constant(2.0)]),
230        ];
231
232        let tree = Tree::with_depth(5, store);
233        assert_eq!(tree.height(), 5);
234    }
235
236    #[test]
237    fn test_tree_with_mixed_arity() {
238        let store = vec![
239            (
240                NodeType::Vertex,
241                vec![
242                    Op::add(),         // Binary operator
243                    Op::constant(1.0), // Constant
244                    Op::sigmoid(),     // Unary operator - gets treated as arity 2
245                ],
246            ),
247            (NodeType::Leaf, vec![Op::constant(2.0)]),
248        ];
249        let tree = Tree::with_depth(3, store);
250
251        // Verify that nodes have appropriate arity based on their type
252        for node in tree.iter_breadth_first() {
253            match node.value() {
254                Op::Fn(name, arity, _) if *name == "add" || *name == "sub" || *name == "mul" => {
255                    assert_eq!(**arity, 2, "Binary operator should have arity 2")
256                }
257                Op::Const(_, _) => assert_eq!(*node.arity(), 0, "Constant should have arity 0"),
258                Op::Fn(name, arity, _) if *name == "sigmoid" => {
259                    assert!(
260                        vec![0, 1, 2].contains(&**arity),
261                        "Unary operator should have arity 0 or 1 or 2"
262                    )
263                }
264                _ => (), // Other ops can be ignored for this test
265            }
266        }
267    }
268
269    #[test]
270    fn test_tree_with_zero_arity() {
271        let store = vec![
272            (NodeType::Vertex, vec![Op::constant(1.0)]), // Constants have zero arity
273            (NodeType::Leaf, vec![Op::constant(2.0)]),
274        ];
275        let tree = Tree::with_depth(2, store);
276
277        // Verify that each vertex node has no children
278        for node in tree.iter_breadth_first() {
279            println!("Node: {:?}", node);
280
281            assert_eq!(*node.arity(), 0, "Vertex node should have zero arity");
282            assert!(
283                node.children().is_none(),
284                "Vertex node should have no children"
285            );
286        }
287    }
288
289    #[test]
290    fn test_tree_with_exact_arity() {
291        let store = vec![
292            (NodeType::Vertex, vec![Op::add(), Op::sub()]), // Binary operators
293            (NodeType::Leaf, vec![Op::constant(1.0), Op::constant(2.0)]),
294        ];
295        let tree = Tree::with_depth(2, store);
296
297        // Verify that each vertex node has exactly 2 children
298        for node in tree.iter_breadth_first() {
299            if node.node_type() == NodeType::Vertex {
300                assert_eq!(node.arity(), Arity::Exact(2));
301                assert_eq!(node.children().unwrap().len(), 2);
302            }
303        }
304    }
305
306    #[test]
307    fn test_tree_with_only_leaf_nodes() {
308        let store = vec![(NodeType::Leaf, vec![Op::constant(1.0), Op::constant(2.0)])];
309        let tree = Tree::with_depth(3, store);
310        assert!(tree.root().is_none());
311        assert_eq!(tree.size(), 0);
312        assert_eq!(tree.height(), 0);
313    }
314
315    #[test]
316    fn test_tree_with_empty_store() {
317        let empty_store: Vec<(NodeType, Vec<Op<f32>>)> = vec![];
318        let tree = Tree::with_depth(3, empty_store);
319
320        // Root will be a default node since no valid nodes were provided
321        assert!(tree.root().is_none());
322        assert_eq!(tree.size(), 0);
323        assert_eq!(tree.height(), 0);
324    }
325
326    #[test]
327    fn test_tree_debug() {
328        let tree = Tree::new(
329            TreeNode::new(Op::add())
330                .attach(TreeNode::new(Op::constant(1.0)))
331                .attach(TreeNode::new(Op::constant(2.0))),
332        );
333
334        let debug_str = format!("{:?}", tree);
335        assert!(debug_str.contains("Tree {"));
336        assert!(debug_str.contains("add"));
337        assert!(debug_str.contains("C"));
338    }
339
340    #[test]
341    fn test_tree_as_ref_as_mut() {
342        let mut tree = Tree::new(
343            TreeNode::new(Op::add())
344                .attach(TreeNode::new(Op::constant(1.0)))
345                .attach(TreeNode::new(Op::constant(2.0))),
346        );
347
348        // Test AsRef
349        let root_ref: &TreeNode<Op<f32>> = tree.as_ref();
350        assert_eq!(root_ref.value(), &Op::add());
351        assert_eq!(root_ref.children().unwrap().len(), 2);
352
353        // Test AsMut
354        let root_mut: &mut TreeNode<Op<f32>> = tree.as_mut();
355        assert_eq!(root_mut.value(), &Op::add());
356
357        root_mut
358            .children_mut()
359            .unwrap()
360            .push(TreeNode::new(Op::constant(3.0))); // Add a new child
361        assert_eq!(root_mut.children().unwrap().len(), 3); // Now should have 3 children
362        // assert!(!tree.as_ref().is_valid()); // Invalid since we added a child without updating size
363    }
364
365    #[test]
366    fn test_tree_root_operations() {
367        // Test root operations on empty tree
368        let mut empty_tree = Tree::<Op<f32>>::default();
369        assert!(empty_tree.root().is_none());
370        assert!(empty_tree.root_mut().is_none());
371        assert!(empty_tree.take_root().is_none());
372
373        // Test root operations on non-empty tree
374        let tree = Tree::new(
375            TreeNode::new(Op::add())
376                .attach(TreeNode::new(Op::constant(1.0)))
377                .attach(TreeNode::new(Op::constant(2.0))),
378        );
379
380        // Test root()
381        let root = tree.root().unwrap();
382        assert_eq!(root.value(), &Op::add());
383        assert_eq!(root.children().unwrap().len(), 2);
384
385        // Test take_root()
386        let root = tree.take_root().unwrap();
387        assert_eq!(root.value(), &Op::add());
388    }
389
390    #[test]
391    #[cfg(feature = "serde")]
392    fn test_tree_can_serde() {
393        use crate::Eval;
394
395        let store = vec![
396            (
397                NodeType::Vertex,
398                vec![
399                    Op::add(),
400                    Op::sub(),
401                    Op::mul(),
402                    Op::div(),
403                    Op::sigmoid(),
404                    Op::tanh(),
405                ],
406            ),
407            (
408                NodeType::Leaf,
409                vec![Op::constant(1.0), Op::constant(2.0), Op::var(0)],
410            ),
411        ];
412
413        let tree = Tree::with_depth(5, store);
414
415        let eval_before = tree.eval(&[3.0]);
416
417        let serialized = serde_json::to_string(&tree).expect("Failed to serialize tree");
418        let deserialized: Tree<Op<f32>> =
419            serde_json::from_str(&serialized).expect("Failed to deserialize tree");
420
421        let eval_after = deserialized.eval(&[3.0]);
422
423        assert_eq!(
424            eval_before, eval_after,
425            "Tree evaluation should match before and after serialization"
426        );
427        assert_eq!(tree, deserialized);
428    }
429}