radiate_gp/collections/eval.rs
1/// [Eval] trait is used to evaluate a [Tree] or [Graph] of node's.
2/// It is implemented directly on the GP structures to allow for easy and dynamic
3/// evaluation of the structures with a given input.
4///
5/// The [Eval] trait and subsequent method is used to transform the `Input` into
6/// the `Output`. This is extremely useful for evaluating the [Graph] or [Tree] with a given input
7/// as traversing each can be very slow or sometimes cumbersome to do manually.
8///
9/// # Example
10/// ```rust
11/// use radiate_gp::{Op, Eval, TreeNode};
12///
13/// let root = TreeNode::new(Op::add())
14/// .attach(
15/// TreeNode::new(Op::mul())
16/// .attach(TreeNode::new(Op::constant(2.0)))
17/// .attach(TreeNode::new(Op::constant(3.0))),
18/// )
19/// .attach(
20/// TreeNode::new(Op::add())
21/// .attach(TreeNode::new(Op::constant(2.0)))
22/// .attach(TreeNode::new(Op::var(0))),
23/// );
24///
25/// // And the result of evaluating this tree with an input of `1` would be:
26/// let result = root.eval(&vec![1_f32]);
27/// assert_eq!(result, 9.0);
28/// ```
29/// This creates a `Tree` that looks like:
30/// ```text
31/// +
32/// / \
33/// * +
34/// / \ / \
35/// 2 3 2 x
36/// ```
37/// Where `x` is the first variable in the input.
38///
39/// This can also be thought of (and is functionally equivalent) as:
40/// ```text
41/// f(x) = (2 * 3) + (2 + x)
42/// ```
43pub trait Eval<I: ?Sized, O> {
44 fn eval(&self, input: &I) -> O;
45}
46
47pub trait EvalMut<I: ?Sized, O> {
48 fn eval_mut(&mut self, input: &I) -> O;
49}
50
51impl<I, O, T> EvalMut<I, O> for T
52where
53 I: ?Sized,
54 T: Eval<I, O>,
55{
56 #[inline]
57 fn eval_mut(&mut self, input: &I) -> O {
58 self.eval(input)
59 }
60}
61
62/// [EvalInto] trait is used to evaluate anything that implements the trait into a mutable buffer.
63/// In some cases where we can cache a buffer to write into, this can avoid allocations and massively
64/// improve performance. Just like the [Eval] & [EvalMut] traits the mutable version offers a way for the
65/// implementing type to mutate its internal state if needed. These are also implemented for the GP structures.
66///
67/// # Example
68/// ```rust
69/// use radiate_gp::*;
70///
71/// // Create a simple graph that adds two inputs together
72/// // This is functionaly equivalent to f(x, y) = x + y
73/// let store = node_store! {
74/// Input => vec![Op::var(0), Op::var(1)],
75/// Output => vec![Op::add()]
76/// };
77///
78/// let mut graph = Graph::directed(2, 1, store);
79///
80/// // Define a buffer to store the output
81/// let mut output_buffer = vec![vec![0.0]];
82/// graph.eval_into(&vec![vec![5.0, 10.0]], &mut output_buffer);
83///
84/// // Check the output - it should be 5 + 10 = 15
85/// assert_eq!(output_buffer[0][0], 15.0);
86/// ```
87pub trait EvalInto<I: ?Sized, O: ?Sized> {
88 fn eval_into(&self, input: &I, buffer: &mut O);
89}
90
91pub trait EvalIntoMut<I: ?Sized, O: ?Sized> {
92 fn eval_into_mut(&mut self, input: &I, buffer: &mut O);
93}
94
95impl<I, O, T> EvalIntoMut<I, O> for T
96where
97 I: ?Sized,
98 O: ?Sized,
99 T: EvalInto<I, O>,
100{
101 #[inline]
102 fn eval_into_mut(&mut self, input: &I, buffer: &mut O) {
103 self.eval_into(input, buffer)
104 }
105}
106
107/// --- Blanket Implementations ---
108///
109/// Below are blanket implementations for closures to make it easy to use them
110/// wherever an [Eval] or [EvalInto] is required.
111impl<F, I, O> EvalInto<I, O> for F
112where
113 I: ?Sized,
114 O: ?Sized,
115 F: Fn(&I, &mut O),
116{
117 #[inline]
118 fn eval_into(&self, input: &I, buffer: &mut O) {
119 (self)(input, buffer)
120 }
121}
122
123/// [Eval] implementation for closures that take an input and return an output.
124impl<F, I, O> Eval<I, O> for F
125where
126 I: ?Sized,
127 F: Fn(&I) -> O,
128{
129 #[inline]
130 fn eval(&self, input: &I) -> O {
131 (self)(input)
132 }
133}