1use super::conclusion_index::ConclusionIndex;
4use super::goal::{Goal, GoalManager, GoalStatus};
5use super::query::{ProofTrace, QueryParser, QueryResult, QueryStats};
6use super::search::{
7 BreadthFirstSearch, DepthFirstSearch, IterativeDeepeningSearch, SearchStrategy,
8};
9use crate::errors::Result;
10use crate::{Facts, KnowledgeBase};
11use std::sync::Arc;
12
13#[derive(Debug, Clone)]
15pub struct BackwardConfig {
16 pub max_depth: usize,
18
19 pub strategy: SearchStrategy,
21
22 pub enable_memoization: bool,
24
25 pub max_solutions: usize,
27}
28
29impl Default for BackwardConfig {
30 fn default() -> Self {
31 Self {
32 max_depth: 10,
33 strategy: SearchStrategy::DepthFirst,
34 enable_memoization: true,
35 max_solutions: 1,
36 }
37 }
38}
39
40pub struct BackwardEngine {
42 knowledge_base: Arc<KnowledgeBase>,
43 config: BackwardConfig,
44 goal_manager: GoalManager,
45 conclusion_index: ConclusionIndex,
47}
48
49impl BackwardEngine {
50 pub fn new(kb: KnowledgeBase) -> Self {
52 let rules = kb.get_rules();
53 let conclusion_index = ConclusionIndex::from_rules(&rules);
54
55 Self {
56 knowledge_base: Arc::new(kb),
57 config: BackwardConfig::default(),
58 goal_manager: GoalManager::default(),
59 conclusion_index,
60 }
61 }
62
63 pub fn with_config(kb: KnowledgeBase, config: BackwardConfig) -> Self {
65 let rules = kb.get_rules();
66 let conclusion_index = ConclusionIndex::from_rules(&rules);
67
68 Self {
69 knowledge_base: Arc::new(kb),
70 goal_manager: GoalManager::new(config.max_depth),
71 config,
72 conclusion_index,
73 }
74 }
75
76 pub fn set_config(&mut self, config: BackwardConfig) {
78 self.goal_manager = GoalManager::new(config.max_depth);
79 self.config = config;
80 }
81
82 pub fn query(&mut self, query_str: &str, facts: &mut Facts) -> Result<QueryResult> {
95 self.query_with_rete_engine(query_str, facts, None)
97 }
98
99 pub fn query_with_rete_engine(
101 &mut self,
102 query_str: &str,
103 facts: &mut Facts,
104 rete_engine: Option<
105 std::sync::Arc<std::sync::Mutex<crate::rete::propagation::IncrementalEngine>>,
106 >,
107 ) -> Result<QueryResult> {
108 let mut goal = QueryParser::parse(query_str)
110 .map_err(|e| crate::errors::RuleEngineError::ParseError { message: e })?;
111
112 if self.config.enable_memoization {
114 if let Some(cached) = self.goal_manager.is_cached(query_str) {
115 return Ok(if cached {
116 QueryResult::success(
117 goal.bindings.to_map(), ProofTrace::from_goal(&goal),
119 QueryStats::default(),
120 )
121 } else {
122 QueryResult::failure(vec![], QueryStats::default())
123 });
124 }
125 }
126
127 self.find_candidate_rules(&mut goal)?;
129
130 let search_result = match self.config.strategy {
132 SearchStrategy::DepthFirst => {
133 let mut dfs = DepthFirstSearch::new_with_engine(
134 self.config.max_depth,
135 (*self.knowledge_base).clone(),
136 rete_engine.clone(),
137 )
138 .with_max_solutions(self.config.max_solutions);
139 dfs.search_with_execution(&mut goal, facts, &self.knowledge_base)
140 }
141 SearchStrategy::BreadthFirst => {
142 let mut bfs = BreadthFirstSearch::new_with_engine(
143 self.config.max_depth,
144 (*self.knowledge_base).clone(),
145 rete_engine.clone(),
146 );
147 bfs.search_with_execution(&mut goal, facts, &self.knowledge_base)
148 }
149 SearchStrategy::Iterative => {
150 let mut ids = IterativeDeepeningSearch::new_with_engine(
151 self.config.max_depth,
152 (*self.knowledge_base).clone(),
153 rete_engine.clone(),
154 );
155 ids.search_with_execution(&mut goal, facts, &self.knowledge_base)
156 }
157 };
158
159 if self.config.enable_memoization {
161 self.goal_manager
162 .cache_result(query_str.to_string(), search_result.success);
163 }
164
165 let stats = QueryStats {
167 goals_explored: search_result.goals_explored,
168 rules_evaluated: search_result.path.len(),
169 max_depth: search_result.max_depth_reached,
170 duration_ms: None,
171 };
172
173 Ok(if search_result.success {
174 QueryResult::success_with_solutions(
176 search_result.bindings,
177 ProofTrace::from_goal(&goal),
178 stats,
179 search_result.solutions,
180 )
181 } else {
182 QueryResult::failure(self.find_missing_facts(&goal), stats)
183 })
184 }
185
186 fn find_candidate_rules(&self, goal: &mut Goal) -> Result<()> {
191 let candidates = self.conclusion_index.find_candidates(&goal.pattern);
193
194 for rule_name in candidates {
196 goal.add_candidate_rule(rule_name);
197 }
198
199 if goal.candidate_rules.is_empty() {
202 for rule in self.knowledge_base.get_rules() {
203 if self.rule_could_prove_goal(&rule, goal) {
204 goal.add_candidate_rule(rule.name.clone());
205 }
206 }
207 }
208
209 Ok(())
210 }
211
212 fn rule_could_prove_goal(&self, rule: &crate::engine::rule::Rule, goal: &Goal) -> bool {
214 if goal.pattern.contains(&rule.name) {
219 return true;
220 }
221
222 for action in &rule.actions {
224 match action {
225 crate::types::ActionType::Set { field, .. } if goal.pattern.contains(field) => {
226 return true;
227 }
228 crate::types::ActionType::MethodCall { object, method, .. }
229 if (goal.pattern.contains(object) || goal.pattern.contains(method)) =>
230 {
231 return true;
232 }
233 _ => {}
234 }
235 }
236
237 false
238 }
239
240 fn find_missing_facts(&self, goal: &Goal) -> Vec<String> {
242 let mut missing = Vec::new();
243
244 Self::collect_missing_recursive(goal, &mut missing);
246
247 missing
248 }
249
250 fn collect_missing_recursive(goal: &Goal, missing: &mut Vec<String>) {
251 if goal.status == GoalStatus::Unprovable {
252 missing.push(goal.pattern.clone());
253 }
254
255 for sub_goal in &goal.sub_goals {
256 Self::collect_missing_recursive(sub_goal, missing);
257 }
258 }
259
260 pub fn explain_why(&mut self, query_str: &str, facts: &mut Facts) -> Result<String> {
262 let result = self.query(query_str, facts)?;
263
264 if result.provable {
265 Ok(format!(
266 "Goal '{}' is PROVABLE\nProof trace:\n{:#?}",
267 query_str, result.proof_trace
268 ))
269 } else {
270 Ok(format!(
271 "Goal '{}' is NOT provable\nMissing facts: {:?}",
272 query_str, result.missing_facts
273 ))
274 }
275 }
276
277 pub fn config(&self) -> &BackwardConfig {
279 &self.config
280 }
281
282 pub fn knowledge_base(&self) -> &KnowledgeBase {
284 &self.knowledge_base
285 }
286
287 pub fn index_stats(&self) -> super::conclusion_index::IndexStats {
289 self.conclusion_index.stats()
290 }
291
292 pub fn rebuild_index(&mut self) {
294 let rules = self.knowledge_base.get_rules();
295 self.conclusion_index = ConclusionIndex::from_rules(&rules);
296 }
297
298 pub fn query_aggregate(
324 &mut self,
325 query: &str,
326 facts: &mut Facts,
327 ) -> Result<crate::types::Value> {
328 use super::aggregation::{apply_aggregate, parse_aggregate_query};
329
330 let agg_query = parse_aggregate_query(query)?;
332
333 let original_max = self.config.max_solutions;
335 self.config.max_solutions = usize::MAX;
336
337 let result = self.query(&agg_query.pattern, facts)?;
339
340 self.config.max_solutions = original_max;
342
343 let value = apply_aggregate(&agg_query.function, &result.solutions)?;
345
346 Ok(value)
347 }
348}
349
350#[cfg(test)]
351#[allow(deprecated)]
352mod tests {
353 use super::*;
354 use crate::types::Value;
355 use crate::KnowledgeBase;
356
357 #[test]
358 fn test_engine_creation() {
359 let kb = KnowledgeBase::new("test");
360 let engine = BackwardEngine::new(kb);
361
362 assert_eq!(engine.config.max_depth, 10);
363 assert_eq!(engine.config.strategy, SearchStrategy::DepthFirst);
364 }
365
366 #[test]
367 fn test_config_customization() {
368 let kb = KnowledgeBase::new("test");
369 let config = BackwardConfig {
370 max_depth: 5,
371 strategy: SearchStrategy::BreadthFirst,
372 enable_memoization: false,
373 max_solutions: 10,
374 };
375
376 let engine = BackwardEngine::with_config(kb, config);
377 assert_eq!(engine.config.max_depth, 5);
378 assert_eq!(engine.config.strategy, SearchStrategy::BreadthFirst);
379 assert!(!engine.config.enable_memoization);
380 }
381
382 #[test]
383 fn test_query_simple() {
384 let kb = KnowledgeBase::new("test");
385 let mut engine = BackwardEngine::new(kb);
386 let mut facts = Facts::new();
387
388 let result = engine.query("User.IsVIP == true", &mut facts);
390 assert!(result.is_ok());
391 }
392
393 #[test]
394 fn test_function_call_condition_len() {
395 use crate::engine::rule::{Condition, ConditionGroup, Rule};
396 use crate::types::{ActionType, Operator};
397
398 let kb = KnowledgeBase::new("test");
399
400 let conditions = ConditionGroup::Single(Condition::with_function(
402 "len".to_string(),
403 vec!["User.Name".to_string()],
404 Operator::GreaterThan,
405 Value::Number(3.0),
406 ));
407 let actions = vec![ActionType::Set {
408 field: "User.HasLongName".to_string(),
409 value: Value::Boolean(true),
410 }];
411
412 let rule = Rule::new("CheckNameLength".to_string(), conditions, actions);
413 let _ = kb.add_rule(rule);
414
415 let mut engine = BackwardEngine::new(kb);
416 let mut facts = Facts::new();
417 facts.set("User.Name", Value::String("John".to_string()));
418
419 let result = engine.query("User.HasLongName == true", &mut facts);
421 assert!(result.is_ok());
422 let query_result = result.unwrap();
423
424 assert!(
426 query_result.provable,
427 "Query should be provable with len() function"
428 );
429 }
430
431 #[test]
432 fn test_function_call_condition_isempty() {
433 use crate::engine::rule::{Condition, ConditionGroup, Rule};
434 use crate::types::{ActionType, Operator};
435
436 let kb = KnowledgeBase::new("test");
437
438 let conditions = ConditionGroup::Single(Condition::with_function(
440 "isEmpty".to_string(),
441 vec!["User.Description".to_string()],
442 Operator::Equal,
443 Value::Boolean(false),
444 ));
445 let actions = vec![ActionType::Set {
446 field: "User.HasDescription".to_string(),
447 value: Value::Boolean(true),
448 }];
449
450 let rule = Rule::new("CheckDescription".to_string(), conditions, actions);
451 let _ = kb.add_rule(rule);
452
453 let mut engine = BackwardEngine::new(kb);
454 let mut facts = Facts::new();
455 facts.set(
456 "User.Description",
457 Value::String("A great user".to_string()),
458 );
459
460 let result = engine.query("User.HasDescription == true", &mut facts);
461 assert!(result.is_ok());
462 let query_result = result.unwrap();
463
464 assert!(
466 query_result.provable,
467 "Query should be provable with isEmpty() function"
468 );
469 }
470
471 #[test]
472 fn test_test_expression_exists() {
473 use crate::engine::rule::{Condition, ConditionExpression, ConditionGroup, Rule};
474 use crate::types::{ActionType, Operator};
475
476 let kb = KnowledgeBase::new("test");
477
478 let condition = Condition {
480 field: "User.Email".to_string(), expression: ConditionExpression::Test {
482 name: "exists".to_string(),
483 args: vec!["User.Email".to_string()],
484 },
485 operator: Operator::Equal,
486 value: Value::Boolean(true),
487 };
488 let conditions = ConditionGroup::Single(condition);
489 let actions = vec![ActionType::Set {
490 field: "User.HasEmail".to_string(),
491 value: Value::Boolean(true),
492 }];
493
494 let rule = Rule::new("CheckEmail".to_string(), conditions, actions);
495 let _ = kb.add_rule(rule);
496
497 let mut engine = BackwardEngine::new(kb);
498 let mut facts = Facts::new();
499 facts.set("User.Email", Value::String("user@example.com".to_string()));
500
501 let result = engine.query("User.HasEmail == true", &mut facts);
502 assert!(result.is_ok());
503 let query_result = result.unwrap();
504
505 assert!(
507 query_result.provable,
508 "Query should be provable with exists test"
509 );
510 }
511
512 #[test]
513 fn test_multifield_count_operation() {
514 use crate::engine::rule::{Condition, ConditionExpression, ConditionGroup, Rule};
515 use crate::types::{ActionType, Operator};
516
517 let kb = KnowledgeBase::new("test");
518
519 let condition = Condition {
521 field: "User.Orders".to_string(), expression: ConditionExpression::MultiField {
523 field: "User.Orders".to_string(),
524 operation: "count".to_string(),
525 variable: None,
526 },
527 operator: Operator::GreaterThan,
528 value: Value::Number(5.0),
529 };
530 let conditions = ConditionGroup::Single(condition);
531 let actions = vec![ActionType::Set {
532 field: "User.IsFrequentBuyer".to_string(),
533 value: Value::Boolean(true),
534 }];
535
536 let rule = Rule::new("CheckFrequentBuyer".to_string(), conditions, actions);
537 let _ = kb.add_rule(rule);
538
539 let mut engine = BackwardEngine::new(kb);
540 let mut facts = Facts::new();
541
542 let orders = Value::Array(vec![
544 Value::Number(1.0),
545 Value::Number(2.0),
546 Value::Number(3.0),
547 Value::Number(4.0),
548 Value::Number(5.0),
549 Value::Number(6.0),
550 ]);
551 facts.set("User.Orders", orders);
552
553 let result = engine.query("User.IsFrequentBuyer == true", &mut facts);
554 assert!(result.is_ok());
555 let query_result = result.unwrap();
556
557 assert!(
559 query_result.provable,
560 "Query should be provable with count multifield operation"
561 );
562 }
563
564 #[test]
565 fn test_fact_derivation_basic() {
566 use crate::engine::rule::{Condition, ConditionGroup, Rule};
567 use crate::types::{ActionType, Operator};
568
569 let kb = KnowledgeBase::new("test");
570
571 let conditions = ConditionGroup::Single(Condition::new(
573 "User.Age".to_string(),
574 Operator::GreaterThan,
575 Value::Number(18.0),
576 ));
577 let actions = vec![ActionType::Set {
578 field: "User.IsAdult".to_string(),
579 value: Value::Boolean(true),
580 }];
581
582 let rule = Rule::new("DetermineAdult".to_string(), conditions, actions);
583 let _ = kb.add_rule(rule);
584
585 let mut engine = BackwardEngine::new(kb);
586 let mut facts = Facts::new();
587 facts.set("User.Age", Value::Number(25.0));
588
589 let result = engine.query("User.IsAdult == true", &mut facts);
591 assert!(result.is_ok());
592 let query_result = result.unwrap();
593
594 assert!(
595 query_result.provable,
596 "Fact should be derived by rule action"
597 );
598 }
599
600 #[test]
601 fn test_rule_chaining_two_levels() {
602 use crate::engine::rule::{Condition, ConditionGroup, Rule};
603 use crate::types::{ActionType, LogicalOperator, Operator};
604
605 let kb = KnowledgeBase::new("test");
606
607 let conditions1 = ConditionGroup::Single(Condition::new(
609 "User.LoyaltyPoints".to_string(),
610 Operator::GreaterThan,
611 Value::Number(100.0),
612 ));
613 let actions1 = vec![ActionType::Set {
614 field: "User.IsVIP".to_string(),
615 value: Value::Boolean(true),
616 }];
617 let rule1 = Rule::new("DetermineVIP".to_string(), conditions1, actions1);
618
619 let conditions2 = ConditionGroup::Compound {
621 left: Box::new(ConditionGroup::Single(Condition::new(
622 "User.IsVIP".to_string(),
623 Operator::Equal,
624 Value::Boolean(true),
625 ))),
626 operator: LogicalOperator::And,
627 right: Box::new(ConditionGroup::Single(Condition::new(
628 "Order.Amount".to_string(),
629 Operator::LessThan,
630 Value::Number(10000.0),
631 ))),
632 };
633 let actions2 = vec![ActionType::Set {
634 field: "Order.AutoApproved".to_string(),
635 value: Value::Boolean(true),
636 }];
637 let rule2 = Rule::new("AutoApproveVIP".to_string(), conditions2, actions2);
638
639 let _ = kb.add_rule(rule1);
640 let _ = kb.add_rule(rule2);
641
642 let mut engine = BackwardEngine::new(kb);
643 let mut facts = Facts::new();
644 facts.set("User.LoyaltyPoints", Value::Number(150.0));
645 facts.set("Order.Amount", Value::Number(5000.0));
646
647 let result = engine.query("Order.AutoApproved == true", &mut facts);
655 assert!(result.is_ok());
656 let query_result = result.unwrap();
657
658 assert!(
659 query_result.provable,
660 "Query should be provable through 2-level rule chaining"
661 );
662 }
663
664 #[test]
665 fn test_fact_derivation_with_log_action() {
666 use crate::engine::rule::{Condition, ConditionGroup, Rule};
667 use crate::types::{ActionType, Operator};
668
669 let kb = KnowledgeBase::new("test");
670
671 let conditions = ConditionGroup::Single(Condition::new(
673 "User.Score".to_string(),
674 Operator::GreaterThan,
675 Value::Number(90.0),
676 ));
677 let actions = vec![
678 ActionType::Log {
679 message: "High score achieved!".to_string(),
680 },
681 ActionType::Set {
682 field: "User.HasHighScore".to_string(),
683 value: Value::Boolean(true),
684 },
685 ];
686
687 let rule = Rule::new("HighScoreRule".to_string(), conditions, actions);
688 let _ = kb.add_rule(rule);
689
690 let mut engine = BackwardEngine::new(kb);
691 let mut facts = Facts::new();
692 facts.set("User.Score", Value::Number(95.0));
693
694 let result = engine.query("User.HasHighScore == true", &mut facts);
695 assert!(result.is_ok());
696 let query_result = result.unwrap();
697
698 assert!(
700 query_result.provable,
701 "Query should be provable with log action"
702 );
703 }
704}