rust_rule_engine/backward/
search.rs1use super::goal::{Goal, GoalStatus};
4use super::rule_executor::RuleExecutor;
5use crate::Facts;
6use crate::types::Value;
7use crate::KnowledgeBase;
8use crate::engine::rule::Rule;
9use std::collections::VecDeque;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum SearchStrategy {
14 DepthFirst,
17
18 BreadthFirst,
21
22 Iterative,
25}
26
27#[derive(Debug)]
29pub struct SearchResult {
30 pub success: bool,
32
33 pub path: Vec<String>,
35
36 pub goals_explored: usize,
38
39 pub max_depth_reached: usize,
41
42 pub bindings: std::collections::HashMap<String, Value>,
44}
45
46impl SearchResult {
47 pub fn success(path: Vec<String>, goals_explored: usize, max_depth: usize) -> Self {
49 Self {
50 success: true,
51 path,
52 goals_explored,
53 max_depth_reached: max_depth,
54 bindings: std::collections::HashMap::new(),
55 }
56 }
57
58 pub fn failure(goals_explored: usize, max_depth: usize) -> Self {
60 Self {
61 success: false,
62 path: Vec::new(),
63 goals_explored,
64 max_depth_reached: max_depth,
65 bindings: std::collections::HashMap::new(),
66 }
67 }
68}
69
70pub struct DepthFirstSearch {
72 max_depth: usize,
73 goals_explored: usize,
74 path: Vec<String>,
75 executor: RuleExecutor,
76}
77
78impl DepthFirstSearch {
79 pub fn new(max_depth: usize, kb: KnowledgeBase) -> Self {
81 Self {
82 max_depth,
83 goals_explored: 0,
84 path: Vec::new(),
85 executor: RuleExecutor::new(kb),
86 }
87 }
88
89 pub fn search_with_execution(&mut self, goal: &mut Goal, facts: &Facts, kb: &KnowledgeBase) -> SearchResult {
91 self.goals_explored = 0;
92 self.path.clear();
93
94 let success = self.search_recursive_with_execution(goal, facts, kb, 0);
95
96 SearchResult {
97 success,
98 path: self.path.clone(),
99 goals_explored: self.goals_explored,
100 max_depth_reached: goal.depth,
101 bindings: goal.bindings.clone(),
102 }
103 }
104
105 pub fn search(&mut self, goal: &mut Goal, _facts: &Facts) -> SearchResult {
107 self.goals_explored = 0;
108 self.path.clear();
109
110 let success = self.search_recursive(goal, 0);
111
112 SearchResult {
113 success,
114 path: self.path.clone(),
115 goals_explored: self.goals_explored,
116 max_depth_reached: goal.depth,
117 bindings: goal.bindings.clone(),
118 }
119 }
120
121 fn search_recursive_with_execution(
123 &mut self,
124 goal: &mut Goal,
125 facts: &Facts,
126 kb: &KnowledgeBase,
127 depth: usize
128 ) -> bool {
129 self.goals_explored += 1;
130
131 if depth > self.max_depth {
133 goal.status = GoalStatus::Unprovable;
134 return false;
135 }
136
137 if self.check_goal_in_facts(goal, facts) {
139 goal.status = GoalStatus::Proven;
140 return true;
141 }
142
143 if goal.status == GoalStatus::InProgress {
145 goal.status = GoalStatus::Unprovable;
146 return false;
147 }
148
149 goal.status = GoalStatus::InProgress;
150 goal.depth = depth;
151
152 for rule_name in goal.candidate_rules.clone() {
154 self.path.push(rule_name.clone());
155
156 if let Some(rule) = kb.get_rule(&rule_name) {
158 if self.check_rule_conditions(&rule, facts) {
160 goal.status = GoalStatus::Proven;
162 return true;
163 }
164
165 }
169
170 self.path.pop();
171 }
172
173 for sub_goal in &mut goal.sub_goals {
175 if !self.search_recursive_with_execution(sub_goal, facts, kb, depth + 1) {
176 goal.status = GoalStatus::Unprovable;
177 return false;
178 }
179 }
180
181 if goal.candidate_rules.is_empty() && goal.sub_goals.is_empty() {
183 goal.status = GoalStatus::Unprovable;
184 return false;
185 }
186
187 goal.status = GoalStatus::Proven;
188 true
189 }
190
191 fn check_goal_in_facts(&self, goal: &Goal, facts: &Facts) -> bool {
193 let pattern = &goal.pattern;
195
196 if let Some(eq_pos) = pattern.find("==") {
198 let field = pattern[..eq_pos].trim();
199 let expected = pattern[eq_pos + 2..].trim();
200
201 if let Some(actual) = facts.get(field) {
202 return self.value_matches(&actual, expected);
203 }
204 return false;
205 }
206
207 if let Some(ne_pos) = pattern.find("!=") {
208 let field = pattern[..ne_pos].trim();
209 let not_expected = pattern[ne_pos + 2..].trim();
210
211 if let Some(actual) = facts.get(field) {
212 return !self.value_matches(&actual, not_expected);
213 }
214 return true;
216 }
217
218 false
219 }
220
221 fn value_matches(&self, value: &Value, expected: &str) -> bool {
223 match value {
224 Value::Boolean(b) => {
225 expected == "true" && *b || expected == "false" && !*b
226 }
227 Value::String(s) => {
228 s == expected || s == expected.trim_matches('"')
229 }
230 Value::Number(n) => {
231 expected.parse::<f64>().map(|e| (n - e).abs() < 0.0001).unwrap_or(false)
232 }
233 _ => false,
234 }
235 }
236
237 fn check_rule_conditions(&self, rule: &Rule, facts: &Facts) -> bool {
239 self.executor.evaluate_conditions(&rule.conditions, facts).unwrap_or(false)
241 }
242
243 fn search_recursive(&mut self, goal: &mut Goal, depth: usize) -> bool {
245 self.goals_explored += 1;
246
247 if depth > self.max_depth {
249 goal.status = GoalStatus::Unprovable;
250 return false;
251 }
252
253 if goal.status == GoalStatus::InProgress {
255 goal.status = GoalStatus::Unprovable;
256 return false;
257 }
258
259 goal.status = GoalStatus::InProgress;
261 goal.depth = depth;
262
263 for rule_name in goal.candidate_rules.clone() {
265 self.path.push(rule_name.clone());
266
267 goal.status = GoalStatus::Proven;
281 return true;
282 }
283
284 for sub_goal in &mut goal.sub_goals {
286 if !self.search_recursive(sub_goal, depth + 1) {
287 goal.status = GoalStatus::Unprovable;
288 return false;
289 }
290 }
291
292 if goal.sub_goals.is_empty() && goal.candidate_rules.is_empty() {
294 goal.status = GoalStatus::Unprovable;
295 return false;
296 }
297
298 goal.status = GoalStatus::Proven;
299 true
300 }
301}
302
303pub struct BreadthFirstSearch {
305 max_depth: usize,
306 goals_explored: usize,
307 executor: RuleExecutor,
308}
309
310impl BreadthFirstSearch {
311 pub fn new(max_depth: usize, kb: KnowledgeBase) -> Self {
313 Self {
314 max_depth,
315 goals_explored: 0,
316 executor: RuleExecutor::new(kb),
317 }
318 }
319
320 pub fn search_with_execution(&mut self, root_goal: &mut Goal, facts: &Facts, kb: &KnowledgeBase) -> SearchResult {
322 self.goals_explored = 0;
323 let mut queue = VecDeque::new();
324 let mut path = Vec::new();
325 let mut max_depth = 0;
326
327 queue.push_back((root_goal as *mut Goal, 0));
328
329 while let Some((goal_ptr, depth)) = queue.pop_front() {
330 let goal = unsafe { &mut *goal_ptr };
332
333 self.goals_explored += 1;
334 max_depth = max_depth.max(depth);
335
336 if depth > self.max_depth {
337 continue;
338 }
339
340 goal.depth = depth;
341
342 if self.check_goal_in_facts(goal, facts) {
344 goal.status = GoalStatus::Proven;
345 continue;
346 }
347
348 for rule_name in goal.candidate_rules.clone() {
350 path.push(rule_name.clone());
351
352 if let Some(rule) = kb.get_rule(&rule_name) {
354 if self.check_rule_conditions(&rule, facts) {
356 goal.status = GoalStatus::Proven;
357 break;
358 }
359 }
360 }
361
362 for sub_goal in &mut goal.sub_goals {
364 queue.push_back((sub_goal as *mut Goal, depth + 1));
365 }
366 }
367
368 let success = root_goal.is_proven();
369
370 SearchResult {
371 success,
372 path,
373 goals_explored: self.goals_explored,
374 max_depth_reached: max_depth,
375 bindings: root_goal.bindings.clone(),
376 }
377 }
378
379 fn check_goal_in_facts(&self, goal: &Goal, facts: &Facts) -> bool {
381 let pattern = &goal.pattern;
382
383 if let Some(eq_pos) = pattern.find("==") {
384 let field = pattern[..eq_pos].trim();
385 let expected = pattern[eq_pos + 2..].trim();
386
387 if let Some(actual) = facts.get(field) {
388 return self.value_matches(&actual, expected);
389 }
390 return false;
391 }
392
393 if let Some(ne_pos) = pattern.find("!=") {
394 let field = pattern[..ne_pos].trim();
395 let not_expected = pattern[ne_pos + 2..].trim();
396
397 if let Some(actual) = facts.get(field) {
398 return !self.value_matches(&actual, not_expected);
399 }
400 return true;
401 }
402
403 false
404 }
405
406 fn value_matches(&self, value: &Value, expected: &str) -> bool {
408 match value {
409 Value::Boolean(b) => {
410 expected == "true" && *b || expected == "false" && !*b
411 }
412 Value::String(s) => {
413 s == expected || s == expected.trim_matches('"')
414 }
415 Value::Number(n) => {
416 expected.parse::<f64>().map(|e| (n - e).abs() < 0.0001).unwrap_or(false)
417 }
418 _ => false,
419 }
420 }
421
422 fn check_rule_conditions(&self, rule: &Rule, facts: &Facts) -> bool {
424 self.executor.evaluate_conditions(&rule.conditions, facts).unwrap_or(false)
426 }
427
428 pub fn search(&mut self, root_goal: &mut Goal, _facts: &Facts) -> SearchResult {
430 self.goals_explored = 0;
431 let mut queue = VecDeque::new();
432 let mut path = Vec::new();
433 let mut max_depth = 0;
434
435 queue.push_back((root_goal as *mut Goal, 0));
436
437 while let Some((goal_ptr, depth)) = queue.pop_front() {
438 let goal = unsafe { &mut *goal_ptr };
440
441 self.goals_explored += 1;
442 max_depth = max_depth.max(depth);
443
444 if depth > self.max_depth {
445 continue;
446 }
447
448 goal.depth = depth;
449
450 for rule_name in &goal.candidate_rules {
452 path.push(rule_name.clone());
453 }
454
455 for sub_goal in &mut goal.sub_goals {
457 queue.push_back((sub_goal as *mut Goal, depth + 1));
458 }
459
460 if !goal.candidate_rules.is_empty() || goal.all_subgoals_proven() {
462 goal.status = GoalStatus::Proven;
463 }
464 }
465
466 let success = root_goal.is_proven();
467
468 SearchResult {
469 success,
470 path,
471 goals_explored: self.goals_explored,
472 max_depth_reached: max_depth,
473 bindings: root_goal.bindings.clone(),
474 }
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 #[test]
483 fn test_search_strategies() {
484 assert_eq!(SearchStrategy::DepthFirst, SearchStrategy::DepthFirst);
485 assert_ne!(SearchStrategy::DepthFirst, SearchStrategy::BreadthFirst);
486 }
487
488 #[test]
489 fn test_search_result_creation() {
490 let success = SearchResult::success(vec!["Rule1".to_string()], 5, 3);
491 assert!(success.success);
492 assert_eq!(success.path.len(), 1);
493 assert_eq!(success.goals_explored, 5);
494
495 let failure = SearchResult::failure(10, 5);
496 assert!(!failure.success);
497 assert!(failure.path.is_empty());
498 }
499
500 #[test]
501 fn test_depth_first_search_creation() {
502 let kb = KnowledgeBase::new("test");
503 let dfs = DepthFirstSearch::new(10, kb);
504 assert_eq!(dfs.max_depth, 10);
505 assert_eq!(dfs.goals_explored, 0);
506 }
507
508 #[test]
509 fn test_depth_first_search_simple() {
510 let kb = KnowledgeBase::new("test");
511 let mut dfs = DepthFirstSearch::new(10, kb);
512 let facts = Facts::new();
513
514 let mut goal = Goal::new("test".to_string());
515 goal.add_candidate_rule("TestRule".to_string());
516
517 let result = dfs.search(&mut goal, &facts);
518
519 assert!(result.success);
520 assert!(goal.is_proven());
521 assert!(result.goals_explored > 0);
522 }
523
524 #[test]
525 fn test_breadth_first_search() {
526 let kb = KnowledgeBase::new("test");
527 let mut bfs = BreadthFirstSearch::new(10, kb);
528 let facts = Facts::new();
529
530 let mut goal = Goal::new("test".to_string());
531 goal.add_candidate_rule("TestRule".to_string());
532
533 let result = bfs.search(&mut goal, &facts);
534
535 assert!(result.success);
536 assert_eq!(result.goals_explored, 1);
537 }
538}