From 8ab9599048639098ebbbcae675582611db7f1a06 Mon Sep 17 00:00:00 2001 From: orxfun Date: Thu, 17 Apr 2025 04:49:29 +0200 Subject: [PATCH 01/15] mutable traversal example is initiated --- examples/mutable_traversal.rs | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 examples/mutable_traversal.rs diff --git a/examples/mutable_traversal.rs b/examples/mutable_traversal.rs new file mode 100644 index 0000000..6fded71 --- /dev/null +++ b/examples/mutable_traversal.rs @@ -0,0 +1,70 @@ +use orx_tree::*; + +struct Inputs {/* placeholder: useful immutable inputs */} + +/// Returns the tree below and index to node 3. +/// +/// ``` +/// 1 +/// ╱ ╲ +/// ╱ ╲ +/// 2 3 +/// ╱ ╲ ╱ ╲ +/// 4 5 6 7 +/// | | ╱ ╲ +/// 8 9 10 11 +/// ``` +fn init_tree() -> (Tree>, NodeIdx>) { + let mut tree = DynTree::new(1u64); + + let mut root = tree.root_mut(); + let [id2, id3] = root.push_children([2, 3]); + let [id4, _] = tree.node_mut(&id2).push_children([4, 5]); + let _id8 = tree.node_mut(&id4).push_child(8); + let [id6, id7] = tree.node_mut(&id3).push_children([6, 7]); + let _id9 = tree.node_mut(&id6).push_child(9); + tree.node_mut(&id7).push_children([10, 11]); + + (tree, id3) +} + +/// Placeholder: counts the number of all children as an example +fn execute_rec(_inputs: &Inputs, node: &Node<'_, Dyn>) -> u64 { + node.walk::().count() as u64 - 1 +} + +fn implementation1() { + let inputs = Inputs {}; + let (mut tree, node_idx) = init_tree(); + + let node = tree.node(&node_idx); + let children_ids = node.children().map(|child| child.idx()).collect::>(); + + let mut new_children = vec![]; + for node_id in children_ids { + let node = tree.node(&node_id); + let value = execute_rec(&inputs, &node); + new_children.push(value); + } + + let mut node = tree.node_mut(&node_idx); + for child in new_children { /* use children and node here */ } +} + +fn implementation2() { + let inputs = Inputs {}; + let (mut tree, node_idx) = init_tree(); + + let node = tree.node_mut(&node_idx); + + let mut new_children = vec![]; + for child in node.children() { + let value = execute_rec(&inputs, &child); + new_children.push(value); + } + + let mut node = tree.node_mut(&node_idx); + for child in new_children { /* use children and node here */ } +} + +fn main() {} From baf6d0d74a0d3c46aaddc2a86f220c8d0e906927 Mon Sep 17 00:00:00 2001 From: orxfun Date: Thu, 17 Apr 2025 05:00:51 +0200 Subject: [PATCH 02/15] remove unnecessary let binding --- examples/mutable_traversal.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/mutable_traversal.rs b/examples/mutable_traversal.rs index 6fded71..ef172a1 100644 --- a/examples/mutable_traversal.rs +++ b/examples/mutable_traversal.rs @@ -63,7 +63,6 @@ fn implementation2() { new_children.push(value); } - let mut node = tree.node_mut(&node_idx); for child in new_children { /* use children and node here */ } } From 6cf259ef77d9c56f692ac3a34f7425846058fa2d Mon Sep 17 00:00:00 2001 From: orxfun Date: Thu, 17 Apr 2025 15:24:47 +0200 Subject: [PATCH 03/15] mutable traversal example revised wrt the feedback --- examples/mutable_traversal.rs | 194 +++++++++++++++++++++++++--------- 1 file changed, 143 insertions(+), 51 deletions(-) diff --git a/examples/mutable_traversal.rs b/examples/mutable_traversal.rs index ef172a1..9ddd740 100644 --- a/examples/mutable_traversal.rs +++ b/examples/mutable_traversal.rs @@ -1,69 +1,161 @@ use orx_tree::*; +use std::fmt::Display; -struct Inputs {/* placeholder: useful immutable inputs */} - -/// Returns the tree below and index to node 3. -/// -/// ``` -/// 1 -/// ╱ ╲ -/// ╱ ╲ -/// 2 3 -/// ╱ ╲ ╱ ╲ -/// 4 5 6 7 -/// | | ╱ ╲ -/// 8 9 10 11 -/// ``` -fn init_tree() -> (Tree>, NodeIdx>) { - let mut tree = DynTree::new(1u64); - - let mut root = tree.root_mut(); - let [id2, id3] = root.push_children([2, 3]); - let [id4, _] = tree.node_mut(&id2).push_children([4, 5]); - let _id8 = tree.node_mut(&id4).push_child(8); - let [id6, id7] = tree.node_mut(&id3).push_children([6, 7]); - let _id9 = tree.node_mut(&id6).push_child(9); - tree.node_mut(&id7).push_children([10, 11]); - - (tree, id3) +const INPUTS_COUNT: usize = 2; + +#[derive(Debug)] +enum Instruction { + Input(usize), + Add, + AddI { val: f32 }, } -/// Placeholder: counts the number of all children as an example -fn execute_rec(_inputs: &Inputs, node: &Node<'_, Dyn>) -> u64 { - node.walk::().count() as u64 - 1 +impl Display for Instruction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Input(x) => write!(f, "Input({})", x), + Self::Add => write!(f, "Add"), + Self::AddI { val } => write!(f, "AddI({})", val), + } + } } -fn implementation1() { - let inputs = Inputs {}; - let (mut tree, node_idx) = init_tree(); +#[derive(Debug)] +struct InstructionNode { + instruction: Instruction, + value: f32, +} - let node = tree.node(&node_idx); - let children_ids = node.children().map(|child| child.idx()).collect::>(); +impl InstructionNode { + fn new(instruction: Instruction, value: f32) -> Self { + Self { instruction, value } + } +} - let mut new_children = vec![]; - for node_id in children_ids { - let node = tree.node(&node_id); - let value = execute_rec(&inputs, &node); - new_children.push(value); +impl Display for InstructionNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.instruction { + Instruction::Input(x) => write!(f, "Input({}) => {}", x, self.value), + Instruction::Add => write!(f, "Add => {}", self.value), + Instruction::AddI { val } => write!(f, "AddI({}) => {}", val, self.value), + } } +} - let mut node = tree.node_mut(&node_idx); - for child in new_children { /* use children and node here */ } +#[derive(Debug)] +struct MyTree { + tree: DynTree, } -fn implementation2() { - let inputs = Inputs {}; - let (mut tree, node_idx) = init_tree(); +impl MyTree { + fn example() -> Self { + let mut tree = DynTree::new(InstructionNode::new(Instruction::AddI { val: 100.0 }, 0.0)); + + let mut n0 = tree.root_mut(); + let [n1, n2] = n0.push_children([ + InstructionNode::new(Instruction::Input(1), 0.0), + InstructionNode::new(Instruction::AddI { val: 2.0 }, 0.0), + ]); + let _n3 = tree + .node_mut(&n1) + .push_child(InstructionNode::new(Instruction::Input(0), 0.0)); + let [_n4, _n5] = tree.node_mut(&n2).push_children([ + InstructionNode::new(Instruction::Add, 0.0), + InstructionNode::new(Instruction::AddI { val: 5.0 }, 0.0), + ]); + + Self { tree } + } + + fn execute_rec( + &mut self, + inputs: &[f32; INPUTS_COUNT], + node_idx: NodeIdx>, + ) -> f32 { + let node = self.tree.node(&node_idx); + + let children_ids = node.children().map(|child| child.idx()).collect::>(); + let mut children = vec![]; + + for node in children_ids { + let value = self.execute_rec(inputs, node); + children.push(value); + } + + let mut node = self.tree.node_mut(&node_idx); - let node = tree.node_mut(&node_idx); + let new_value = match node.data().instruction { + Instruction::Input(i) => inputs[i], + Instruction::Add => children.into_iter().sum(), + Instruction::AddI { val } => children.into_iter().sum::() + val, + }; + (*node.data_mut()).value = new_value; - let mut new_children = vec![]; - for child in node.children() { - let value = execute_rec(&inputs, &child); - new_children.push(value); + new_value } +} + +fn execute<'a>( + inputs: &[f32; INPUTS_COUNT], + mut node: NodeMut<'a, Dyn>, +) -> (NodeMut<'a, Dyn>, f32) { + let num_children = node.num_children(); + + let new_value = match node.data().instruction { + Instruction::Input(i) => inputs[i], + Instruction::Add => { + let mut value = 0.0; + for i in 0..num_children { + let child = node.into_child_mut(i).unwrap(); + let (child, child_value) = execute(inputs, child); + value += child_value; + node = child.into_parent_mut().unwrap(); + } + value + } + Instruction::AddI { val } => { + let mut value = val; + for i in 0..num_children { + let child = node.into_child_mut(i).unwrap(); + let (child, child_value) = execute(inputs, child); + value += child_value; + node = child.into_parent_mut().unwrap(); + } + value + } + }; + + (*node.data_mut()).value = new_value; - for child in new_children { /* use children and node here */ } + (node, new_value) } -fn main() {} +fn impl_over_children_idx() { + let inputs = [10.0, 20.0]; + + let mut tree = MyTree::example(); + let node_idx = tree.tree.root().idx(); + + println!("\n\n# IMPL OVER CHILDREN INDICES"); + println!("\ninputs = {:?}\n", &inputs); + println!("Before execute:\n{}\n", &tree.tree); + tree.execute_rec(&inputs, node_idx); + println!("After execute:\n{}\n", &tree.tree); +} + +fn impl_over_children_mut() { + let inputs = [10.0, 20.0]; + + let mut tree = MyTree::example(); + + println!("\n\n# IMPL WITH INTO_CHILD_MUT & INTO_PARENT_MUT"); + println!("\ninputs = {:?}\n", &inputs); + println!("Before execute:\n{}\n", &tree.tree); + execute(&inputs, tree.tree.root_mut()); + println!("After execute:\n{}\n", &tree.tree); +} + +fn main() { + impl_over_children_idx(); + impl_over_children_mut(); +} From ecb15589df5b9ba11f90964cfb99e5f9e6428d53 Mon Sep 17 00:00:00 2001 From: orxfun Date: Thu, 17 Apr 2025 16:37:05 +0200 Subject: [PATCH 04/15] impl_over_children_mut is fixed --- examples/mutable_traversal.rs | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/examples/mutable_traversal.rs b/examples/mutable_traversal.rs index 9ddd740..8ee10a0 100644 --- a/examples/mutable_traversal.rs +++ b/examples/mutable_traversal.rs @@ -101,35 +101,24 @@ fn execute<'a>( ) -> (NodeMut<'a, Dyn>, f32) { let num_children = node.num_children(); + let mut children_sum = 0.0; + for i in 0..num_children { + let child = node.into_child_mut(i).unwrap(); + let (child, child_value) = execute(inputs, child); + children_sum += child_value; + node = child.into_parent_mut().unwrap(); + } + let new_value = match node.data().instruction { Instruction::Input(i) => inputs[i], - Instruction::Add => { - let mut value = 0.0; - for i in 0..num_children { - let child = node.into_child_mut(i).unwrap(); - let (child, child_value) = execute(inputs, child); - value += child_value; - node = child.into_parent_mut().unwrap(); - } - value - } - Instruction::AddI { val } => { - let mut value = val; - for i in 0..num_children { - let child = node.into_child_mut(i).unwrap(); - let (child, child_value) = execute(inputs, child); - value += child_value; - node = child.into_parent_mut().unwrap(); - } - value - } + Instruction::Add => children_sum, + Instruction::AddI { val } => val + children_sum, }; (*node.data_mut()).value = new_value; (node, new_value) } - fn impl_over_children_idx() { let inputs = [10.0, 20.0]; From 2f96b9f1470b96d51dc78620ae611e26ca2f306c Mon Sep 17 00:00:00 2001 From: orxfun Date: Thu, 17 Apr 2025 16:38:42 +0200 Subject: [PATCH 05/15] space --- examples/mutable_traversal.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/mutable_traversal.rs b/examples/mutable_traversal.rs index 8ee10a0..e3b0481 100644 --- a/examples/mutable_traversal.rs +++ b/examples/mutable_traversal.rs @@ -119,6 +119,7 @@ fn execute<'a>( (node, new_value) } + fn impl_over_children_idx() { let inputs = [10.0, 20.0]; From 23fd148a6a7dddddc8caf51d69246dc356fdda45 Mon Sep 17 00:00:00 2001 From: orxfun Date: Fri, 25 Apr 2025 17:08:10 +0200 Subject: [PATCH 06/15] recursive_set method is introduced --- src/node_mut.rs | 168 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 167 insertions(+), 1 deletion(-) diff --git a/src/node_mut.rs b/src/node_mut.rs index ac083d2..bfcfb61 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -1,5 +1,5 @@ use crate::{ - NodeIdx, NodeRef, SubTree, Traverser, Tree, TreeVariant, + NodeIdx, NodeRef, PostOrder, SubTree, Traverser, Tree, TreeVariant, aliases::{Col, N}, iter::ChildrenMutIter, memory::{Auto, MemoryPolicy}, @@ -10,12 +10,15 @@ use crate::{ traversal::{ OverData, OverMut, enumerations::Val, + over::OverPtr, over_mut::{OverItemInto, OverItemMut}, post_order::iter_ptr::PostOrderIterPtr, + traverser_core::TraverserCore, }, tree_node_idx::INVALID_IDX_ERROR, tree_variant::RefsChildren, }; +use alloc::vec::Vec; use core::{fmt::Debug, marker::PhantomData}; use orx_selfref_col::{NodePtr, Refs}; @@ -2455,6 +2458,169 @@ where traverser.into_iter(self) } + // recursive + + /// Recursively sets the data of all nodes belonging to the subtree rooted at this node using the `compute_data` + /// function. + /// + /// The `compute_data` function takes two arguments: + /// + /// * current value (data) of this node, and + /// * slice of values of children of this node that are computed recursively using `compute_data` (*). + /// + /// Then, `compute_data` function reduces this node's current value and new values of its children to the new + /// value of this node. + /// + /// The method is named *recursive* (*) due to the fact that, + /// + /// * before computing the value of this node (or any of its descendants); + /// * values of all its children are computed and set using the `compute_data` function. + /// + /// *Note that this method does not actually make recursive method calls. Instead, it internally uses the [`PostOrder`] + /// traverser which ensures that all required values are computed before they are used for another computation. This + /// is a guard against potential stack overflow issues that could be observed in sufficiently large trees.* + /// + /// [`PostOrder`]: crate::PostOrder + /// + /// # Examples + /// + /// In the following example, we set the value of every node to the sum of values of all its descendants. + /// + /// While building the tree, we set only the values of the leaves. + /// We initially set values of all other nodes to zero as a placeholder. + /// Then, we call `recursive_set` to compute them. + /// + /// ``` + /// use orx_tree::*; + /// + /// let mut tree = DynTree::<_>::new(0); + /// let [id1, id2] = tree.root_mut().push_children([0, 0]); + /// tree.node_mut(&id1).push_children([1, 3]); + /// tree.node_mut(&id2).push_children([7, 2, 4]); + /// // 0 + /// // ╱ ╲ + /// // ╱ ╲ + /// // 0 0 + /// // ╱ ╲ ╱|╲ + /// // 1 3 7 2 4 + /// + /// tree.root_mut() + /// .recursive_set( + /// |current_value, children_values| match children_values.is_empty() { + /// true => *current_value, // is a leaf + /// false => children_values.iter().copied().sum(), + /// }, + /// ); + /// // 17 + /// // ╱ ╲ + /// // ╱ ╲ + /// // 4 13 + /// // ╱ ╲ ╱|╲ + /// // 1 3 7 2 4 + /// + /// let bfs: Vec<_> = tree.root().walk::().copied().collect(); + /// assert_eq!(bfs, [17, 4, 13, 1, 3, 7, 2, 4]); + /// ``` + /// + /// The following is a similar example where leaf nodes represent deterministic outcomes of + /// a process. + /// The root represents the current state. + /// The remaining nodes represent intermediate states that we can reach from its parent with + /// the given `probability`. + /// Our task is to compute `expected_value` of each state. + /// + /// Since we know the value of the leaves with certainty, we set them while constructing the + /// tree. Then, we call `recursive_set` to compute the expected value of every other node. + /// + /// ``` + /// use orx_tree::*; + /// + /// #[derive(Clone)] + /// struct State { + /// /// Probability of reaching this state from its parent. + /// probability: f64, + /// /// Expected value of the state; i.e., average of values of all leaves weighted by + /// /// the probability of being reached from this state. + /// expected_value: f64, + /// } + /// + /// fn state(probability: f64, expected_value: f64) -> State { + /// State { + /// probability, + /// expected_value, + /// } + /// } + /// + /// // (1.0, ???) + /// // ╱ ╲ + /// // ╱ ╲ + /// // ╱ ╲ + /// // ╱ ╲ + /// // (.3, ???) (.7, ???) + /// // ╱ ╲ | ╲ + /// // ╱ ╲ | ╲ + /// // (.2, 9) (.8, 2) (.9, 5) (.1, 4) + /// + /// let mut tree = DynTree::<_>::new(state(1.0, 0.0)); + /// + /// let [id1, id2] = tree + /// .root_mut() + /// .push_children([state(0.3, 0.0), state(0.7, 0.0)]); + /// tree.node_mut(&id1) + /// .push_children([state(0.2, 9.0), state(0.8, 2.0)]); + /// tree.node_mut(&id2) + /// .push_children([state(0.9, 5.0), state(0.1, 4.0)]); + /// + /// tree.root_mut() + /// .recursive_set( + /// |current_value, children_values| match children_values.is_empty() { + /// true => current_value.clone(), // is a leaf, we know expected value + /// false => { + /// let expected_value = children_values + /// .iter() + /// .fold(0.0, |a, x| a + x.probability * x.expected_value); + /// state(current_value.probability, expected_value) + /// } + /// }, + /// ); + /// // (1.0, 4.45) + /// // ╱ ╲ + /// // ╱ ╲ + /// // ╱ ╲ + /// // ╱ ╲ + /// // (.3, 3.4) (.7, 4.9) + /// // ╱ ╲ | ╲ + /// // ╱ ╲ | ╲ + /// // (.2, 9) (.8, 2) (.9, 5) (.1, 4) + /// + /// let equals = |a: f64, b: f64| (a - b).abs() < 1e-5; + /// + /// assert!(equals(tree.root().data().expected_value, 4.45)); + /// assert!(equals(tree.node(&id1).data().expected_value, 3.40)); + /// assert!(equals(tree.node(&id2).data().expected_value, 4.90)); + /// ``` + pub fn recursive_set(&mut self, compute_data: impl Fn(&V::Item, &[&V::Item]) -> V::Item) { + let mut iter = PostOrder::::iter_ptr_with_owned_storage(self.node_ptr.clone()); + let mut children_data = Vec::<&V::Item>::new(); + + while let Some(ptr) = iter.next() { + let x: NodePtr<_> = ptr; + let node = unsafe { &mut *x.ptr_mut() }; + let node_data = node.data().expect("is not closed"); + + for child_ptr in node.next().children_ptr() { + let data = unsafe { &*child_ptr.ptr() }.data().expect("is not closed"); + children_data.push(data); + } + + let new_data = compute_data(node_data, &children_data); + + *node.data_mut().expect("is not closed") = new_data; + + children_data.clear(); + } + } + // subtree /// Creates a subtree view including this node as the root and all of its descendants with their orientation relative From e430ed4f144eb000fbb87c713da172bd56875a40 Mon Sep 17 00:00:00 2001 From: orxfun Date: Fri, 25 Apr 2025 17:08:30 +0200 Subject: [PATCH 07/15] recursive set is added to the example --- examples/mutable_traversal.rs | 29 ++++++++++++++++++++++++++- src/traversal/post_order/traverser.rs | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/examples/mutable_traversal.rs b/examples/mutable_traversal.rs index e3b0481..58414e3 100644 --- a/examples/mutable_traversal.rs +++ b/examples/mutable_traversal.rs @@ -3,7 +3,7 @@ use std::fmt::Display; const INPUTS_COUNT: usize = 2; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] enum Instruction { Input(usize), Add, @@ -145,7 +145,34 @@ fn impl_over_children_mut() { println!("After execute:\n{}\n", &tree.tree); } +fn impl_recursive_set() { + let inputs = [10.0, 20.0]; + + let mut tree = MyTree::example(); + + println!("\n\n# IMPL WITH RECURSIVE_SET"); + println!("\ninputs = {:?}\n", &inputs); + println!("Before execute:\n{}\n", &tree.tree); + + tree.tree + .root_mut() + .recursive_set(|node_data, children_data| { + let instruction = node_data.instruction; + let children_sum: f32 = children_data.iter().map(|x| x.value).sum(); + let value = match node_data.instruction { + Instruction::Input(i) => inputs[i], + Instruction::Add => children_sum, + Instruction::AddI { val } => val + children_sum, + }; + + InstructionNode { instruction, value } + }); + + println!("After execute:\n{}\n", &tree.tree); +} + fn main() { impl_over_children_idx(); impl_over_children_mut(); + impl_recursive_set(); } diff --git a/src/traversal/post_order/traverser.rs b/src/traversal/post_order/traverser.rs index 46648c7..09e8042 100644 --- a/src/traversal/post_order/traverser.rs +++ b/src/traversal/post_order/traverser.rs @@ -13,6 +13,7 @@ use core::marker::PhantomData; /// # Construction /// /// A post order traverser can be created, +/// /// * either by using Default trait and providing its two generic type parameters /// * `PostOrder::<_, OverData>::default()` or `PostOrder::<_, OverDepthSiblingIdxData>::default()`, or /// * `PostOrder::, OverData>::default()` or `PostOrder::, OverDepthSiblingIdxData>::default()` From 1c8ee97cd00e26e771779280cc4c2568a931ed00 Mon Sep 17 00:00:00 2001 From: orxfun Date: Fri, 25 Apr 2025 17:14:13 +0200 Subject: [PATCH 08/15] mutable_traversal examples updated, recursive_set is added --- examples/mutable_traversal.rs | 82 +++++++++++++---------------------- 1 file changed, 31 insertions(+), 51 deletions(-) diff --git a/examples/mutable_traversal.rs b/examples/mutable_traversal.rs index 58414e3..bed7f4c 100644 --- a/examples/mutable_traversal.rs +++ b/examples/mutable_traversal.rs @@ -67,11 +67,7 @@ impl MyTree { Self { tree } } - fn execute_rec( - &mut self, - inputs: &[f32; INPUTS_COUNT], - node_idx: NodeIdx>, - ) -> f32 { + fn execute_rec(&mut self, inputs: &[f32], node_idx: NodeIdx>) -> f32 { let node = self.tree.node(&node_idx); let children_ids = node.children().map(|child| child.idx()).collect::>(); @@ -96,7 +92,7 @@ impl MyTree { } fn execute<'a>( - inputs: &[f32; INPUTS_COUNT], + inputs: &[f32], mut node: NodeMut<'a, Dyn>, ) -> (NodeMut<'a, Dyn>, f32) { let num_children = node.num_children(); @@ -120,59 +116,43 @@ fn execute<'a>( (node, new_value) } -fn impl_over_children_idx() { +fn test_implementation(method: &str, f: impl FnOnce(&[f32], &mut MyTree)) { let inputs = [10.0, 20.0]; let mut tree = MyTree::example(); - let node_idx = tree.tree.root().idx(); - println!("\n\n# IMPL OVER CHILDREN INDICES"); + println!("\n\n# {}", method); println!("\ninputs = {:?}\n", &inputs); println!("Before execute:\n{}\n", &tree.tree); - tree.execute_rec(&inputs, node_idx); - println!("After execute:\n{}\n", &tree.tree); -} - -fn impl_over_children_mut() { - let inputs = [10.0, 20.0]; - - let mut tree = MyTree::example(); - - println!("\n\n# IMPL WITH INTO_CHILD_MUT & INTO_PARENT_MUT"); - println!("\ninputs = {:?}\n", &inputs); - println!("Before execute:\n{}\n", &tree.tree); - execute(&inputs, tree.tree.root_mut()); - println!("After execute:\n{}\n", &tree.tree); -} - -fn impl_recursive_set() { - let inputs = [10.0, 20.0]; - - let mut tree = MyTree::example(); - - println!("\n\n# IMPL WITH RECURSIVE_SET"); - println!("\ninputs = {:?}\n", &inputs); - println!("Before execute:\n{}\n", &tree.tree); - - tree.tree - .root_mut() - .recursive_set(|node_data, children_data| { - let instruction = node_data.instruction; - let children_sum: f32 = children_data.iter().map(|x| x.value).sum(); - let value = match node_data.instruction { - Instruction::Input(i) => inputs[i], - Instruction::Add => children_sum, - Instruction::AddI { val } => val + children_sum, - }; - - InstructionNode { instruction, value } - }); - + f(&inputs, &mut tree); println!("After execute:\n{}\n", &tree.tree); } fn main() { - impl_over_children_idx(); - impl_over_children_mut(); - impl_recursive_set(); + test_implementation("IMPL OVER CHILDREN INDICES", |inputs, tree| { + tree.execute_rec(inputs, tree.tree.root().idx()); + }); + + test_implementation( + "IMPL WITH INTO_CHILD_MUT & INTO_PARENT_MUT", + |inputs, tree| { + execute(&inputs, tree.tree.root_mut()); + }, + ); + + test_implementation("IMPL recursive_set", |inputs, tree| { + tree.tree + .root_mut() + .recursive_set(|node_data, children_data| { + let instruction = node_data.instruction; + let children_sum: f32 = children_data.iter().map(|x| x.value).sum(); + let value = match node_data.instruction { + Instruction::Input(i) => inputs[i], + Instruction::Add => children_sum, + Instruction::AddI { val } => val + children_sum, + }; + + InstructionNode { instruction, value } + }); + }); } From b4e2359ab137ef8c16fcc9a963f4b99ba1a9277b Mon Sep 17 00:00:00 2001 From: orxfun Date: Sat, 26 Apr 2025 19:30:12 +0200 Subject: [PATCH 09/15] clippy fixes --- src/node_mut.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/node_mut.rs b/src/node_mut.rs index bfcfb61..0ee594f 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -2599,11 +2599,12 @@ where /// assert!(equals(tree.node(&id1).data().expected_value, 3.40)); /// assert!(equals(tree.node(&id2).data().expected_value, 4.90)); /// ``` + #[allow(clippy::missing_panics_doc)] pub fn recursive_set(&mut self, compute_data: impl Fn(&V::Item, &[&V::Item]) -> V::Item) { - let mut iter = PostOrder::::iter_ptr_with_owned_storage(self.node_ptr.clone()); + let iter = PostOrder::::iter_ptr_with_owned_storage(self.node_ptr.clone()); let mut children_data = Vec::<&V::Item>::new(); - while let Some(ptr) = iter.next() { + for ptr in iter { let x: NodePtr<_> = ptr; let node = unsafe { &mut *x.ptr_mut() }; let node_data = node.data().expect("is not closed"); From efac6258f7eb071eeb8397476df678e454ce7517 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sat, 26 Apr 2025 19:30:29 +0200 Subject: [PATCH 10/15] example refactoring and documentation for mutable traversal --- examples/mutable_traversal.rs | 192 ++++++++++++++++++++++++---------- 1 file changed, 135 insertions(+), 57 deletions(-) diff --git a/examples/mutable_traversal.rs b/examples/mutable_traversal.rs index bed7f4c..aa7adae 100644 --- a/examples/mutable_traversal.rs +++ b/examples/mutable_traversal.rs @@ -1,8 +1,20 @@ +// # EXAMPLE DEFINITION +// +// This example demonstrates a use case where value of a node is defined +// as a function of the values of its children. Since the value of a child +// of the node also depends on values of its own children, it follows that +// the value of a node is a function of values of all of its descendants. +// +// The task is to compute and set all values of a tree given the values of +// the leaves. +// +// This is a interesting and common case in terms of requiring mutable +// recursive traversal over the tree that can be handled with different +// approaches. Some of these are demonstrated in this example. + use orx_tree::*; use std::fmt::Display; -const INPUTS_COUNT: usize = 2; - #[derive(Debug, Clone, Copy)] enum Instruction { Input(usize), @@ -43,11 +55,11 @@ impl Display for InstructionNode { } #[derive(Debug)] -struct MyTree { +struct Instructions { tree: DynTree, } -impl MyTree { +impl Instructions { fn example() -> Self { let mut tree = DynTree::new(InstructionNode::new(Instruction::AddI { val: 100.0 }, 0.0)); @@ -66,32 +78,32 @@ impl MyTree { Self { tree } } - - fn execute_rec(&mut self, inputs: &[f32], node_idx: NodeIdx>) -> f32 { - let node = self.tree.node(&node_idx); - - let children_ids = node.children().map(|child| child.idx()).collect::>(); - let mut children = vec![]; - - for node in children_ids { - let value = self.execute_rec(inputs, node); - children.push(value); - } - - let mut node = self.tree.node_mut(&node_idx); - - let new_value = match node.data().instruction { - Instruction::Input(i) => inputs[i], - Instruction::Add => children.into_iter().sum(), - Instruction::AddI { val } => children.into_iter().sum::() + val, - }; - (*node.data_mut()).value = new_value; - - new_value - } } -fn execute<'a>( +/// Demonstrates manual mutable and recursive traversal over the tree. +/// +/// Notice that we can freely walk the tree while always having a single +/// mutable reference to one node. This satisfies the borrow checker rules +/// and further allows for calling the function recursively. +/// +/// Note also that, although it is not necessary in this scenario, we are +/// free to change the shape of the tree during our walk by adding nodes, +/// moving around or pruning subtrees, etc. In other words, it enables the +/// greatest freedom while it requires us to make sure that we do not have +/// errors, such as out-of-bounds errors with the `into_child_mut` call. +/// +/// * Pros +/// * Complete freedom to mutate the nodes and the tree structure during +/// the walk. +/// * No intermediate allocation is required; borrow checker rules are +/// satisfied without the need to collect indices. +/// * Cons +/// * Implementor is required to define the walk. This example demonstrates +/// a depth-first walk due to the recursive calls, which is straightforward +/// to implement. +/// * Due to lack of tail-call optimization in rust, this function is likely +/// to encounter stack overflow for very deep trees. +fn recursive_traversal_over_nodes<'a>( inputs: &[f32], mut node: NodeMut<'a, Dyn>, ) -> (NodeMut<'a, Dyn>, f32) { @@ -100,7 +112,7 @@ fn execute<'a>( let mut children_sum = 0.0; for i in 0..num_children { let child = node.into_child_mut(i).unwrap(); - let (child, child_value) = execute(inputs, child); + let (child, child_value) = recursive_traversal_over_nodes(inputs, child); children_sum += child_value; node = child.into_parent_mut().unwrap(); } @@ -116,43 +128,109 @@ fn execute<'a>( (node, new_value) } -fn test_implementation(method: &str, f: impl FnOnce(&[f32], &mut MyTree)) { - let inputs = [10.0, 20.0]; +/// Demonstrates recursive mutable traversal by internally collecting and storing +/// the child node indices. +/// +/// This simplifies the borrow relations and allows for the recursive calls only +/// having a single mutable reference to the tree; however, each recursive call +/// requires an internal allocation. +/// +/// * Pros +/// * Complete freedom to mutate the nodes and the tree structure during +/// the walk. +/// * Cons +/// * Requires to collect indices and results into an internal vector for each +/// recursive call, requiring additional allocation. +/// * Implementor is required to define the walk. This example demonstrates +/// a depth-first walk due to the recursive calls, which is straightforward +/// to implement. +/// * Due to lack of tail-call optimization in rust, this function is likely +/// to encounter stack overflow for very deep trees. +fn recursive_traversal_over_indices( + tree: &mut DynTree, + inputs: &[f32], + node_idx: NodeIdx>, +) -> f32 { + let node = tree.node(&node_idx); + + let children_ids: Vec<_> = node.children().map(|child| child.idx()).collect(); + let children: Vec<_> = children_ids + .into_iter() + .map(|node| recursive_traversal_over_indices(tree, inputs, node)) + .collect(); - let mut tree = MyTree::example(); + let mut node = tree.node_mut(&node_idx); - println!("\n\n# {}", method); - println!("\ninputs = {:?}\n", &inputs); - println!("Before execute:\n{}\n", &tree.tree); - f(&inputs, &mut tree); - println!("After execute:\n{}\n", &tree.tree); + let new_value = match node.data().instruction { + Instruction::Input(i) => inputs[i], + Instruction::Add => children.into_iter().sum(), + Instruction::AddI { val } => children.into_iter().sum::() + val, + }; + (*node.data_mut()).value = new_value; + + new_value } -fn main() { - test_implementation("IMPL OVER CHILDREN INDICES", |inputs, tree| { - tree.execute_rec(inputs, tree.tree.root().idx()); +/// Demonstrates the use of [`recursive_set`] method: +/// +/// *Recursively sets the data of all nodes belonging to the subtree rooted +/// at this node using the compute_data function.* +/// +/// This function fits perfectly to this and similar scenarios where we want +/// to compute values of all nodes of a tree such that the value of a node +/// depends on the values of all of its descendants, and hence the name +/// *recursive*. +/// +/// * Pros +/// * More expressive in the sense that the implementor only defines how the +/// value of a node should be computed given its prior value and values of +/// its children. Iteration is abstracted away. +/// * Despite the name, the implementation actually does not require recursive +/// function calls; and hence, can work with trees of arbitrary depth without +/// the risk of stack overflow. Instead, it internally uses the [`PostOrder`] +/// traverser. +/// * Cons +/// * It only allows to set the data of the nodes; however, does not allow for +/// structural mutations. +/// +/// [`recursive_set`]: orx_tree::NodeMut::recursive_set +/// [`PostOrder`]: orx_tree::PostOrder +fn recursive_set(inputs: &[f32], mut node: NodeMut>) { + node.recursive_set(|node_data, children_data| { + let instruction = node_data.instruction; + let children_sum: f32 = children_data.iter().map(|x| x.value).sum(); + let value = match node_data.instruction { + Instruction::Input(i) => inputs[i], + Instruction::Add => children_sum, + Instruction::AddI { val } => val + children_sum, + }; + + InstructionNode { instruction, value } }); +} + +fn main() { + fn test_implementation(method: &str, f: impl FnOnce(&[f32], &mut Instructions)) { + let inputs = [10.0, 20.0]; + let mut instructions = Instructions::example(); + println!("\n\n### {}", method); + f(&inputs, &mut instructions); + println!("\n{}\n", &instructions.tree); + } test_implementation( - "IMPL WITH INTO_CHILD_MUT & INTO_PARENT_MUT", - |inputs, tree| { - execute(&inputs, tree.tree.root_mut()); + "recursive_traversal_over_indices", + |inputs, instructions| { + let root_idx = instructions.tree.root().idx(); + recursive_traversal_over_indices(&mut instructions.tree, inputs, root_idx); }, ); - test_implementation("IMPL recursive_set", |inputs, tree| { - tree.tree - .root_mut() - .recursive_set(|node_data, children_data| { - let instruction = node_data.instruction; - let children_sum: f32 = children_data.iter().map(|x| x.value).sum(); - let value = match node_data.instruction { - Instruction::Input(i) => inputs[i], - Instruction::Add => children_sum, - Instruction::AddI { val } => val + children_sum, - }; - - InstructionNode { instruction, value } - }); + test_implementation("recursive_traversal_over_nodes", |inputs, instructions| { + recursive_traversal_over_nodes(&inputs, instructions.tree.root_mut()); + }); + + test_implementation("recursive_set", |inputs, instructions| { + recursive_set(inputs, instructions.tree.root_mut()); }); } From 4f5e1ff1e7d58d1619ddc11720d855dfdf8f0185 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sat, 26 Apr 2025 19:46:38 +0200 Subject: [PATCH 11/15] recursive_set documentation is revised --- src/node_mut.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/node_mut.rs b/src/node_mut.rs index 0ee594f..9c66494 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -2463,22 +2463,24 @@ where /// Recursively sets the data of all nodes belonging to the subtree rooted at this node using the `compute_data` /// function. /// + /// Alternatively, sets values of all nodes when the value of a node is defined as a function of its prior value + /// and values of its children (hence, it recursively depends on values of all descendants). + /// /// The `compute_data` function takes two arguments: /// /// * current value (data) of this node, and - /// * slice of values of children of this node that are computed recursively using `compute_data` (*). + /// * slice of values of children of this node that are computed recursively using `compute_data` (*); /// - /// Then, `compute_data` function reduces this node's current value and new values of its children to the new - /// value of this node. + /// and then, computes the new value of this node. /// /// The method is named *recursive* (*) due to the fact that, /// - /// * before computing the value of this node (or any of its descendants); - /// * values of all its children are computed and set using the `compute_data` function. + /// * before computing the value of this node; + /// * values of all of its children are also computed and set using the `compute_data` function. /// - /// *Note that this method does not actually make recursive method calls. Instead, it internally uses the [`PostOrder`] + /// *Note that this method does **not** actually make recursive method calls. Instead, it internally uses the [`PostOrder`] /// traverser which ensures that all required values are computed before they are used for another computation. This - /// is a guard against potential stack overflow issues that could be observed in sufficiently large trees.* + /// is a guard against potential stack overflow issues, and hence, can be used for trees of arbitrary depth.* /// /// [`PostOrder`]: crate::PostOrder /// @@ -2605,8 +2607,7 @@ where let mut children_data = Vec::<&V::Item>::new(); for ptr in iter { - let x: NodePtr<_> = ptr; - let node = unsafe { &mut *x.ptr_mut() }; + let node = unsafe { &mut *ptr.ptr_mut() }; let node_data = node.data().expect("is not closed"); for child_ptr in node.next().children_ptr() { From 74f8c4f55884832a92080c5cc92fc3615e34ac2d Mon Sep 17 00:00:00 2001 From: orxfun Date: Sat, 26 Apr 2025 19:48:18 +0200 Subject: [PATCH 12/15] example renamed as mutable_recursive_traversal --- examples/{mutable_traversal.rs => mutable_recursive_traversal.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{mutable_traversal.rs => mutable_recursive_traversal.rs} (100%) diff --git a/examples/mutable_traversal.rs b/examples/mutable_recursive_traversal.rs similarity index 100% rename from examples/mutable_traversal.rs rename to examples/mutable_recursive_traversal.rs From e91996e0f80a25a3f0d4065a71eab81ccaa3e36a Mon Sep 17 00:00:00 2001 From: orxfun Date: Sat, 26 Apr 2025 19:51:14 +0200 Subject: [PATCH 13/15] recursive_set documentation is revised --- src/node_mut.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/node_mut.rs b/src/node_mut.rs index 9c66494..9c463f2 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -2463,8 +2463,10 @@ where /// Recursively sets the data of all nodes belonging to the subtree rooted at this node using the `compute_data` /// function. /// - /// Alternatively, sets values of all nodes when the value of a node is defined as a function of its prior value - /// and values of its children (hence, it recursively depends on values of all descendants). + /// This method provides an expressive way to update the values of a tree where value of a node is a function of + /// its prior value and values of its children. Since the values of its children subsequently depend on their own + /// children, it immediately follows that the value of the node depends on values of all of its descendants that + /// must be computed to be able to compute the node's value. /// /// The `compute_data` function takes two arguments: /// From 6b16e1ce034374dd2d277c2b3d4534005da64847 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 27 Apr 2025 19:37:29 +0200 Subject: [PATCH 14/15] example is linked in the readme --- README.md | 4 ++++ examples/mutable_recursive_traversal.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index a298561..b6662cd 100644 --- a/README.md +++ b/README.md @@ -375,6 +375,10 @@ let remaining_bfs: Vec<_> = tree.root().walk::().copied().collect(); assert_eq!(remaining_bfs, [1, 3, 6, 9]); ``` +### More Examples + +* [mutable_recursive_traversal](https://github.com/orxfun/orx-tree/blob/main/examples/mutable_recursive_traversal.rs) demonstrates different approaches to achieve a recursive mutation of all nodes in the tree. + ## Contributing Contributions are welcome! If you notice an error, have a question or think something could be added or improved, please open an [issue](https://github.com/orxfun/orx-tree/issues/new) or create a PR. diff --git a/examples/mutable_recursive_traversal.rs b/examples/mutable_recursive_traversal.rs index aa7adae..b87c225 100644 --- a/examples/mutable_recursive_traversal.rs +++ b/examples/mutable_recursive_traversal.rs @@ -1,5 +1,7 @@ // # EXAMPLE DEFINITION // +// cargo run --example mutable_recursive_traversal +// // This example demonstrates a use case where value of a node is defined // as a function of the values of its children. Since the value of a child // of the node also depends on values of its own children, it follows that From 3fa41ded65dabd9822edefe473527ea3b935c5b4 Mon Sep 17 00:00:00 2001 From: orxfun Date: Sun, 27 Apr 2025 19:37:44 +0200 Subject: [PATCH 15/15] version number incremented --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 808538b..0ab6576 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orx-tree" -version = "1.4.0" +version = "1.5.0" edition = "2024" authors = ["orxfun "] description = "A beautiful tree 🌳 with convenient and efficient growth, mutation and traversal features."