1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
//! GP tree node trait and recursive node enum.
//!
//! [`GpNode`] is the user-facing trait: implement it on your own enum to define
//! the function and terminal set for genetic programming.
//!
//! [`Node<N>`] is the library-provided recursive tree structure. It stores a
//! tree of `GpNode` values and supports depth computation, node counting, and
//! an iterative `Drop` implementation to prevent stack overflows on deep trees.
use crateGaError;
use Rng;
use mem;
/// Trait that every GP primitive set must implement.
///
/// Implement `GpNode` on your own enum to define the function set (non-terminals
/// with arity > 0) and the terminal set (leaves with arity == 0). The engine uses
/// this trait to build, evaluate, and mutate expression trees.
///
/// # Required methods
///
/// | Method | Description |
/// |--------|-------------|
/// | `arity` | Number of child arguments this node consumes |
/// | `evaluate` | Evaluate the node given pre-evaluated child values |
/// | `sample_random_terminal` | Produce a fresh terminal node (for ERC support) |
/// | `all_functions` | Enumerate all non-terminal variants (for point mutation) |
///
/// # Example
///
/// ```rust,no_run
/// // no_run: GpNode trait implementation example — illustrative API usage
/// use genetic_algorithms::gp::GpNode;
/// use rand::Rng;
///
/// #[derive(Clone, Debug)]
/// enum MyNode { Add, Mul, Const(f64) }
///
/// impl GpNode for MyNode {
/// fn arity(&self) -> usize { match self { MyNode::Add | MyNode::Mul => 2, MyNode::Const(_) => 0 } }
/// fn evaluate(&self, args: &[f64]) -> f64 {
/// match self {
/// MyNode::Add => args[0] + args[1],
/// MyNode::Mul => args[0] * args[1],
/// MyNode::Const(v) => *v,
/// }
/// }
/// fn sample_random_terminal(rng: &mut impl Rng) -> Self { MyNode::Const(rng.gen_range(-1.0..=1.0)) }
/// fn all_functions() -> Vec<Self> { vec![MyNode::Add, MyNode::Mul] }
/// }
/// ```
/// A recursive expression tree node.
///
/// `Node<N>` stores a tree of `N: GpNode` values. Function nodes hold their
/// children in a `Vec<Box<Node<N>>>` (one child per `N::arity()`). Terminal
/// nodes hold a single leaf value.
///
/// # Memory safety
///
/// `Node<N>` implements a custom iterative [`Drop`] to avoid stack overflow
/// when dropping very deep trees. The default recursive drop would overflow the
/// stack for trees with hundreds of thousands of nodes.
///
/// # Serde and deep trees
///
/// When the `serde` feature is enabled, `Node<N>` derives `Serialize` and
/// `Deserialize` via the standard serde derive macros. For trees up to depth 64
/// (the GP engine's recommended maximum), stack usage is well within typical
/// system limits. For extremely deep trees (depth > ~500), use
/// `serde_stacker::Serializer` / `serde_stacker::Deserializer` wrappers at the
/// serialization call site to grow the stack dynamically.
///
/// # Security note
///
/// Checkpoint files should be treated as trusted input. Deserialization does
/// not bound allocation — an attacker-controlled JSON blob with an unbounded
/// `children` array could exhaust process memory.
// ---------------------------------------------------------------------------
// Shared helpers (pub(crate)) used by crossover.rs and mutation.rs
// ---------------------------------------------------------------------------
/// Checks that `node` does not violate the configured depth or size limits.
///
/// Returns [`GaError::TreeDepthExceeded`] if `node.depth() > max_depth` or
/// [`GaError::TreeSizeExceeded`] if `node.node_count() > max_node_count`.
///
/// Depth is checked first: a tree that is too deep is also too large, so the
/// depth error gives a more precise diagnosis.
pub
/// Grows a random tree up to `max_depth` using the "grow" method.
///
/// At each level there is a 50% probability of generating a terminal (leaf)
/// unless `max_depth <= 1`, in which case a terminal is always returned.
/// If `N::all_functions()` returns an empty list, a terminal is always returned
/// regardless of depth.
///
/// # Arguments
///
/// * `max_depth` — maximum allowed depth for the generated subtree
/// * `rng` — random number generator
pub
/// Custom iterative `Drop` to prevent stack overflow on very deep trees.
///
/// The default recursive drop would overflow the call stack for trees with
/// depth in the thousands. This implementation drains children iteratively
/// using a worklist.