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