1use std::fmt::Debug;
47
48use super::map::{ExplorationMap, GraphExplorationMap, GraphMap, MapNodeId, MapState};
49use super::mutation::{MapUpdateResult, MutationInput};
50use super::node_rules::Rules;
51use super::operator::{MutationLogic, Operator};
52use super::selection::SelectionLogic;
53use crate::actions::ActionsConfig;
54use crate::online_stats::SwarmStats;
55
56pub use super::mutation::MutationInput as SpaceMutationInput;
58
59pub struct ExplorationSpaceV2<N, E, S, R, M, Sel>
77where
78 N: Debug + Clone,
79 E: Debug + Clone,
80 S: MapState,
81 R: Rules,
82 M: MutationLogic<N, E, S, R>,
83 Sel: SelectionLogic<N, E, S>,
84{
85 map: GraphMap<N, E, S>,
87
88 operator: Operator<M, Sel, N, E, S, R>,
90
91 dependency_graph: Option<super::DependencyGraph>,
93
94 completed: bool,
96
97 actions_config: ActionsConfig,
99}
100
101impl<N, E, S, R, M, Sel> ExplorationSpaceV2<N, E, S, R, M, Sel>
102where
103 N: Debug + Clone,
104 E: Debug + Clone,
105 S: MapState + Default,
106 R: Rules + 'static,
107 M: MutationLogic<N, E, S, R>,
108 Sel: SelectionLogic<N, E, S>,
109{
110 pub fn new(operator: Operator<M, Sel, N, E, S, R>) -> Self {
112 Self {
113 map: GraphMap::new(),
114 operator,
115 dependency_graph: None,
116 completed: false,
117 actions_config: ActionsConfig::new(),
118 }
119 }
120
121 pub fn with_dependency_graph(mut self, graph: super::DependencyGraph) -> Self {
123 self.dependency_graph = Some(graph);
124 self
125 }
126
127 pub fn with_actions_config(mut self, config: ActionsConfig) -> Self {
129 self.actions_config = config;
130 self
131 }
132
133 pub fn create_root(&mut self, data: N) -> MapNodeId {
139 self.map.create_root(data, S::default())
140 }
141
142 pub fn apply(&mut self, input: &dyn MutationInput, stats: &SwarmStats) -> Vec<MapUpdateResult> {
147 let updates = self
148 .operator
149 .interpret(input, &self.map, &self.actions_config, stats);
150 self.map.apply_updates(updates, |k| k.to_string())
151 }
152
153 pub fn select(&self, count: usize, stats: &SwarmStats) -> Vec<MapNodeId> {
155 self.operator.select(&self.map, count, stats)
156 }
157
158 pub fn next(&self, stats: &SwarmStats) -> Option<MapNodeId> {
160 self.operator.next(&self.map, stats)
161 }
162
163 pub fn score(&self, action: &str, target: Option<&str>, stats: &SwarmStats) -> f64 {
165 self.operator.score(action, target, stats)
166 }
167
168 pub fn select_nodes(
170 &self,
171 count: usize,
172 stats: &SwarmStats,
173 ) -> Vec<&super::map::MapNode<N, S>> {
174 let ids = self.select(count, stats);
175 self.map.get_nodes(&ids)
176 }
177
178 pub fn initialize(&mut self, initial_contexts: &[&str]) -> Vec<MapUpdateResult> {
180 let root_id = match self.map.root() {
181 Some(id) => id,
182 None => return vec![],
183 };
184
185 if initial_contexts.is_empty() {
186 return vec![];
187 }
188
189 let updates = self.operator.initialize(root_id, initial_contexts);
190 self.map.apply_updates(updates, |k| k.to_string())
191 }
192
193 pub fn get_nodes(&self, ids: &[MapNodeId]) -> Vec<&super::map::MapNode<N, S>> {
199 self.map.get_nodes(ids)
200 }
201
202 pub fn get_node_data(&self, ids: &[MapNodeId]) -> Vec<&N> {
204 self.map.get_node_data(ids)
205 }
206
207 pub fn frontiers(&self) -> Vec<MapNodeId> {
209 self.map.frontiers()
210 }
211
212 pub fn root(&self) -> Option<MapNodeId> {
214 self.map.root()
215 }
216
217 pub fn node_count(&self) -> usize {
219 self.map.node_count()
220 }
221
222 pub fn is_complete(&self) -> bool {
224 self.completed || self.operator.is_complete(&self.map)
225 }
226
227 pub fn has_completed(&self) -> bool {
229 self.completed
230 }
231
232 pub fn mark_completed(&mut self) {
234 self.completed = true;
235 }
236
237 pub fn is_exhausted(&self) -> bool {
239 self.map.frontiers().is_empty()
240 }
241
242 pub fn map(&self) -> &GraphMap<N, E, S> {
244 &self.map
245 }
246
247 pub fn map_mut(&mut self) -> &mut GraphMap<N, E, S> {
249 &mut self.map
250 }
251
252 pub fn operator(&self) -> &Operator<M, Sel, N, E, S, R> {
254 &self.operator
255 }
256
257 pub fn operator_mut(&mut self) -> &mut Operator<M, Sel, N, E, S, R> {
259 &mut self.operator
260 }
261
262 pub fn dependency_graph(&self) -> Option<&super::DependencyGraph> {
264 self.dependency_graph.as_ref()
265 }
266
267 pub fn operator_name(&self) -> String {
269 self.operator.name()
270 }
271
272 pub fn rules(&self) -> &R {
274 self.operator.rules()
275 }
276}
277
278use super::map::MapNodeState;
283use super::mutation::ActionNodeData;
284use super::operator::RulesBasedMutation;
285use super::selection::{AnySelection, Fifo as FifoSelection};
286
287pub type ActionSpace<R> =
289 ExplorationSpaceV2<ActionNodeData, String, MapNodeState, R, RulesBasedMutation, FifoSelection>;
290
291pub type ConfigurableSpace<R> =
295 ExplorationSpaceV2<ActionNodeData, String, MapNodeState, R, RulesBasedMutation, AnySelection>;
296
297#[cfg(test)]
302mod tests {
303 use super::super::mutation::ExplorationResult;
304 use super::super::operator::Operator;
305 use super::super::NodeRules;
306 use super::*;
307
308 struct TestInput {
310 node_id: MapNodeId,
311 action_name: String,
312 target: Option<String>,
313 result: ExplorationResult,
314 }
315
316 impl MutationInput for TestInput {
317 fn node_id(&self) -> MapNodeId {
318 self.node_id
319 }
320
321 fn action_name(&self) -> &str {
322 &self.action_name
323 }
324
325 fn target(&self) -> Option<&str> {
326 self.target.as_deref()
327 }
328
329 fn result(&self) -> &ExplorationResult {
330 &self.result
331 }
332 }
333
334 fn make_test_space() -> ActionSpace<NodeRules> {
335 let rules = NodeRules::for_testing();
336 let operator = Operator::new(RulesBasedMutation::new(), FifoSelection::new(), rules);
337 ExplorationSpaceV2::new(operator)
338 }
339
340 #[test]
341 fn test_exploration_space_v2_basic() {
342 let mut space = make_test_space();
343
344 let root = space.create_root(ActionNodeData::new("root"));
346 assert_eq!(space.node_count(), 1);
347 assert_eq!(space.root(), Some(root));
348
349 assert_eq!(space.frontiers().len(), 1);
351 assert!(!space.is_exhausted());
352 }
353
354 #[test]
355 fn test_exploration_space_v2_apply() {
356 let mut space = make_test_space();
357 let stats = SwarmStats::new();
358
359 let root = space.create_root(ActionNodeData::new("root"));
360
361 let input = TestInput {
363 node_id: root,
364 action_name: "grep".to_string(),
365 target: Some("auth.rs".to_string()),
366 result: ExplorationResult::Success,
367 };
368
369 let results = space.apply(&input, &stats);
370 assert_eq!(results.len(), 3);
372 assert!(matches!(results[0], MapUpdateResult::NodeCreated(_)));
373 assert!(matches!(results[1], MapUpdateResult::NodeCreated(_)));
374 assert!(matches!(results[2], MapUpdateResult::NodeClosed(_)));
375
376 assert_eq!(space.node_count(), 3);
378
379 assert_eq!(space.frontiers().len(), 2);
381 }
382
383 #[test]
384 fn test_exploration_space_v2_select() {
385 let mut space = make_test_space();
386 let stats = SwarmStats::new();
387
388 let root = space.create_root(ActionNodeData::new("root"));
389
390 let input1 = TestInput {
392 node_id: root,
393 action_name: "grep".to_string(),
394 target: Some("a.rs".to_string()),
395 result: ExplorationResult::Success,
396 };
397 space.apply(&input1, &stats);
398
399 let selected = space.select(2, &stats);
401 assert_eq!(selected.len(), 2);
402 }
403
404 #[test]
405 fn test_exploration_space_v2_completion() {
406 let mut space = make_test_space();
407
408 assert!(!space.has_completed()); assert!(space.is_complete()); assert!(space.is_exhausted());
412
413 space.create_root(ActionNodeData::new("task"));
415 assert!(!space.is_complete());
416 assert!(!space.is_exhausted());
417
418 space.mark_completed();
420 assert!(space.has_completed());
421 assert!(space.is_complete()); }
423
424 #[test]
425 fn test_exploration_space_v2_is_complete_operator() {
426 let mut space = make_test_space();
427
428 let root = space.create_root(ActionNodeData::new("task"));
429 assert!(!space.is_complete()); space.map_mut().set_state(root, MapNodeState::Closed);
433 assert!(space.is_complete());
434 assert!(space.is_exhausted());
435 assert!(!space.has_completed()); }
437
438 #[test]
439 fn test_exploration_space_v2_initialize() {
440 let mut space = make_test_space();
441
442 let _root = space.create_root(ActionNodeData::new("task"));
444 assert_eq!(space.node_count(), 1);
445
446 let results = space.initialize(&["auth", "login"]);
448 assert_eq!(results.len(), 4); assert_eq!(space.node_count(), 5);
452
453 let frontiers = space.frontiers();
455 assert_eq!(frontiers.len(), 5);
456 }
457
458 #[test]
459 fn test_exploration_space_v2_initialize_empty_rules() {
460 let rules = NodeRules::new(); let operator = Operator::new(RulesBasedMutation::new(), FifoSelection::new(), rules);
463 let mut space: ActionSpace<NodeRules> = ExplorationSpaceV2::new(operator);
464
465 let _root = space.create_root(ActionNodeData::new("task"));
466
467 let results = space.initialize(&["auth"]);
469 assert!(results.is_empty());
470 assert_eq!(space.node_count(), 1);
471 }
472
473 #[test]
474 fn test_exploration_space_v2_initialize_no_root() {
475 let mut space = make_test_space();
476
477 let results = space.initialize(&["auth"]);
479 assert!(results.is_empty());
480 }
481
482 #[test]
483 fn test_exploration_space_v2_get_nodes() {
484 use crate::actions::Action;
485
486 let mut space = make_test_space();
487 let stats = SwarmStats::new();
488
489 let root = space.create_root(ActionNodeData::new("root"));
491 let input = TestInput {
492 node_id: root,
493 action_name: "grep".to_string(),
494 target: Some("auth.rs".to_string()),
495 result: ExplorationResult::Success,
496 };
497 space.apply(&input, &stats);
498
499 let selected = space.select(3, &stats);
501 assert!(!selected.is_empty());
502
503 let nodes = space.get_nodes(&selected);
505 assert_eq!(nodes.len(), selected.len());
506
507 let data = space.get_node_data(&selected);
509 assert_eq!(data.len(), selected.len());
510
511 let actions: Vec<Action> = data.iter().map(|d| Action::from(*d)).collect();
513 assert_eq!(actions.len(), selected.len());
514 }
515
516 #[test]
517 fn test_exploration_space_v2_operator_name() {
518 let space = make_test_space();
519 assert_eq!(space.operator_name(), "RulesBased+FIFO");
520 }
521}