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