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, .. } => {
226 if goal.pattern.contains(field) {
227 return true;
228 }
229 }
230 crate::types::ActionType::MethodCall { object, method, .. } => {
231 if goal.pattern.contains(object) || goal.pattern.contains(method) {
232 return true;
233 }
234 }
235 _ => {}
236 }
237 }
238
239 false
240 }
241
242 fn find_missing_facts(&self, goal: &Goal) -> Vec<String> {
244 let mut missing = Vec::new();
245
246 Self::collect_missing_recursive(goal, &mut missing);
248
249 missing
250 }
251
252 fn collect_missing_recursive(goal: &Goal, missing: &mut Vec<String>) {
253 if goal.status == GoalStatus::Unprovable {
254 missing.push(goal.pattern.clone());
255 }
256
257 for sub_goal in &goal.sub_goals {
258 Self::collect_missing_recursive(sub_goal, missing);
259 }
260 }
261
262 pub fn explain_why(&mut self, query_str: &str, facts: &mut Facts) -> Result<String> {
264 let result = self.query(query_str, facts)?;
265
266 if result.provable {
267 Ok(format!(
268 "Goal '{}' is PROVABLE\nProof trace:\n{:#?}",
269 query_str, result.proof_trace
270 ))
271 } else {
272 Ok(format!(
273 "Goal '{}' is NOT provable\nMissing facts: {:?}",
274 query_str, result.missing_facts
275 ))
276 }
277 }
278
279 pub fn config(&self) -> &BackwardConfig {
281 &self.config
282 }
283
284 pub fn knowledge_base(&self) -> &KnowledgeBase {
286 &self.knowledge_base
287 }
288
289 pub fn index_stats(&self) -> super::conclusion_index::IndexStats {
291 self.conclusion_index.stats()
292 }
293
294 pub fn rebuild_index(&mut self) {
296 let rules = self.knowledge_base.get_rules();
297 self.conclusion_index = ConclusionIndex::from_rules(&rules);
298 }
299
300 pub fn query_aggregate(
326 &mut self,
327 query: &str,
328 facts: &mut Facts,
329 ) -> Result<crate::types::Value> {
330 use super::aggregation::{apply_aggregate, parse_aggregate_query};
331
332 let agg_query = parse_aggregate_query(query)?;
334
335 let original_max = self.config.max_solutions;
337 self.config.max_solutions = usize::MAX;
338
339 let result = self.query(&agg_query.pattern, facts)?;
341
342 self.config.max_solutions = original_max;
344
345 let value = apply_aggregate(&agg_query.function, &result.solutions)?;
347
348 Ok(value)
349 }
350}
351
352#[cfg(test)]
353#[allow(deprecated)]
354mod tests {
355 use super::*;
356 use crate::types::Value;
357 use crate::KnowledgeBase;
358
359 #[test]
360 fn test_engine_creation() {
361 let kb = KnowledgeBase::new("test");
362 let engine = BackwardEngine::new(kb);
363
364 assert_eq!(engine.config.max_depth, 10);
365 assert_eq!(engine.config.strategy, SearchStrategy::DepthFirst);
366 }
367
368 #[test]
369 fn test_config_customization() {
370 let kb = KnowledgeBase::new("test");
371 let config = BackwardConfig {
372 max_depth: 5,
373 strategy: SearchStrategy::BreadthFirst,
374 enable_memoization: false,
375 max_solutions: 10,
376 };
377
378 let engine = BackwardEngine::with_config(kb, config);
379 assert_eq!(engine.config.max_depth, 5);
380 assert_eq!(engine.config.strategy, SearchStrategy::BreadthFirst);
381 assert!(!engine.config.enable_memoization);
382 }
383
384 #[test]
385 fn test_query_simple() {
386 let kb = KnowledgeBase::new("test");
387 let mut engine = BackwardEngine::new(kb);
388 let mut facts = Facts::new();
389
390 let result = engine.query("User.IsVIP == true", &mut facts);
392 assert!(result.is_ok());
393 }
394
395 #[test]
396 fn test_function_call_condition_len() {
397 use crate::engine::rule::{Condition, ConditionGroup, Rule};
398 use crate::types::{ActionType, Operator};
399
400 let kb = KnowledgeBase::new("test");
401
402 let conditions = ConditionGroup::Single(Condition::with_function(
404 "len".to_string(),
405 vec!["User.Name".to_string()],
406 Operator::GreaterThan,
407 Value::Number(3.0),
408 ));
409 let actions = vec![ActionType::Set {
410 field: "User.HasLongName".to_string(),
411 value: Value::Boolean(true),
412 }];
413
414 let rule = Rule::new("CheckNameLength".to_string(), conditions, actions);
415 let _ = kb.add_rule(rule);
416
417 let mut engine = BackwardEngine::new(kb);
418 let mut facts = Facts::new();
419 facts.set("User.Name", Value::String("John".to_string()));
420
421 let result = engine.query("User.HasLongName == true", &mut facts);
423 assert!(result.is_ok());
424 let query_result = result.unwrap();
425
426 assert!(
428 query_result.provable,
429 "Query should be provable with len() function"
430 );
431 }
432
433 #[test]
434 fn test_function_call_condition_isempty() {
435 use crate::engine::rule::{Condition, ConditionGroup, Rule};
436 use crate::types::{ActionType, Operator};
437
438 let kb = KnowledgeBase::new("test");
439
440 let conditions = ConditionGroup::Single(Condition::with_function(
442 "isEmpty".to_string(),
443 vec!["User.Description".to_string()],
444 Operator::Equal,
445 Value::Boolean(false),
446 ));
447 let actions = vec![ActionType::Set {
448 field: "User.HasDescription".to_string(),
449 value: Value::Boolean(true),
450 }];
451
452 let rule = Rule::new("CheckDescription".to_string(), conditions, actions);
453 let _ = kb.add_rule(rule);
454
455 let mut engine = BackwardEngine::new(kb);
456 let mut facts = Facts::new();
457 facts.set(
458 "User.Description",
459 Value::String("A great user".to_string()),
460 );
461
462 let result = engine.query("User.HasDescription == true", &mut facts);
463 assert!(result.is_ok());
464 let query_result = result.unwrap();
465
466 assert!(
468 query_result.provable,
469 "Query should be provable with isEmpty() function"
470 );
471 }
472
473 #[test]
474 fn test_test_expression_exists() {
475 use crate::engine::rule::{Condition, ConditionExpression, ConditionGroup, Rule};
476 use crate::types::{ActionType, Operator};
477
478 let kb = KnowledgeBase::new("test");
479
480 let condition = Condition {
482 field: "User.Email".to_string(), expression: ConditionExpression::Test {
484 name: "exists".to_string(),
485 args: vec!["User.Email".to_string()],
486 },
487 operator: Operator::Equal,
488 value: Value::Boolean(true),
489 };
490 let conditions = ConditionGroup::Single(condition);
491 let actions = vec![ActionType::Set {
492 field: "User.HasEmail".to_string(),
493 value: Value::Boolean(true),
494 }];
495
496 let rule = Rule::new("CheckEmail".to_string(), conditions, actions);
497 let _ = kb.add_rule(rule);
498
499 let mut engine = BackwardEngine::new(kb);
500 let mut facts = Facts::new();
501 facts.set("User.Email", Value::String("user@example.com".to_string()));
502
503 let result = engine.query("User.HasEmail == true", &mut facts);
504 assert!(result.is_ok());
505 let query_result = result.unwrap();
506
507 assert!(
509 query_result.provable,
510 "Query should be provable with exists test"
511 );
512 }
513
514 #[test]
515 fn test_multifield_count_operation() {
516 use crate::engine::rule::{Condition, ConditionExpression, ConditionGroup, Rule};
517 use crate::types::{ActionType, Operator};
518
519 let kb = KnowledgeBase::new("test");
520
521 let condition = Condition {
523 field: "User.Orders".to_string(), expression: ConditionExpression::MultiField {
525 field: "User.Orders".to_string(),
526 operation: "count".to_string(),
527 variable: None,
528 },
529 operator: Operator::GreaterThan,
530 value: Value::Number(5.0),
531 };
532 let conditions = ConditionGroup::Single(condition);
533 let actions = vec![ActionType::Set {
534 field: "User.IsFrequentBuyer".to_string(),
535 value: Value::Boolean(true),
536 }];
537
538 let rule = Rule::new("CheckFrequentBuyer".to_string(), conditions, actions);
539 let _ = kb.add_rule(rule);
540
541 let mut engine = BackwardEngine::new(kb);
542 let mut facts = Facts::new();
543
544 let orders = Value::Array(vec![
546 Value::Number(1.0),
547 Value::Number(2.0),
548 Value::Number(3.0),
549 Value::Number(4.0),
550 Value::Number(5.0),
551 Value::Number(6.0),
552 ]);
553 facts.set("User.Orders", orders);
554
555 let result = engine.query("User.IsFrequentBuyer == true", &mut facts);
556 assert!(result.is_ok());
557 let query_result = result.unwrap();
558
559 assert!(
561 query_result.provable,
562 "Query should be provable with count multifield operation"
563 );
564 }
565
566 #[test]
567 fn test_fact_derivation_basic() {
568 use crate::engine::rule::{Condition, ConditionGroup, Rule};
569 use crate::types::{ActionType, Operator};
570
571 let kb = KnowledgeBase::new("test");
572
573 let conditions = ConditionGroup::Single(Condition::new(
575 "User.Age".to_string(),
576 Operator::GreaterThan,
577 Value::Number(18.0),
578 ));
579 let actions = vec![ActionType::Set {
580 field: "User.IsAdult".to_string(),
581 value: Value::Boolean(true),
582 }];
583
584 let rule = Rule::new("DetermineAdult".to_string(), conditions, actions);
585 let _ = kb.add_rule(rule);
586
587 let mut engine = BackwardEngine::new(kb);
588 let mut facts = Facts::new();
589 facts.set("User.Age", Value::Number(25.0));
590
591 let result = engine.query("User.IsAdult == true", &mut facts);
593 assert!(result.is_ok());
594 let query_result = result.unwrap();
595
596 assert!(
597 query_result.provable,
598 "Fact should be derived by rule action"
599 );
600 }
601
602 #[test]
603 fn test_rule_chaining_two_levels() {
604 use crate::engine::rule::{Condition, ConditionGroup, Rule};
605 use crate::types::{ActionType, LogicalOperator, Operator};
606
607 let kb = KnowledgeBase::new("test");
608
609 let conditions1 = ConditionGroup::Single(Condition::new(
611 "User.LoyaltyPoints".to_string(),
612 Operator::GreaterThan,
613 Value::Number(100.0),
614 ));
615 let actions1 = vec![ActionType::Set {
616 field: "User.IsVIP".to_string(),
617 value: Value::Boolean(true),
618 }];
619 let rule1 = Rule::new("DetermineVIP".to_string(), conditions1, actions1);
620
621 let conditions2 = ConditionGroup::Compound {
623 left: Box::new(ConditionGroup::Single(Condition::new(
624 "User.IsVIP".to_string(),
625 Operator::Equal,
626 Value::Boolean(true),
627 ))),
628 operator: LogicalOperator::And,
629 right: Box::new(ConditionGroup::Single(Condition::new(
630 "Order.Amount".to_string(),
631 Operator::LessThan,
632 Value::Number(10000.0),
633 ))),
634 };
635 let actions2 = vec![ActionType::Set {
636 field: "Order.AutoApproved".to_string(),
637 value: Value::Boolean(true),
638 }];
639 let rule2 = Rule::new("AutoApproveVIP".to_string(), conditions2, actions2);
640
641 let _ = kb.add_rule(rule1);
642 let _ = kb.add_rule(rule2);
643
644 let mut engine = BackwardEngine::new(kb);
645 let mut facts = Facts::new();
646 facts.set("User.LoyaltyPoints", Value::Number(150.0));
647 facts.set("Order.Amount", Value::Number(5000.0));
648
649 let result = engine.query("Order.AutoApproved == true", &mut facts);
657 assert!(result.is_ok());
658 let query_result = result.unwrap();
659
660 assert!(
661 query_result.provable,
662 "Query should be provable through 2-level rule chaining"
663 );
664 }
665
666 #[test]
667 fn test_fact_derivation_with_log_action() {
668 use crate::engine::rule::{Condition, ConditionGroup, Rule};
669 use crate::types::{ActionType, Operator};
670
671 let kb = KnowledgeBase::new("test");
672
673 let conditions = ConditionGroup::Single(Condition::new(
675 "User.Score".to_string(),
676 Operator::GreaterThan,
677 Value::Number(90.0),
678 ));
679 let actions = vec![
680 ActionType::Log {
681 message: "High score achieved!".to_string(),
682 },
683 ActionType::Set {
684 field: "User.HasHighScore".to_string(),
685 value: Value::Boolean(true),
686 },
687 ];
688
689 let rule = Rule::new("HighScoreRule".to_string(), conditions, actions);
690 let _ = kb.add_rule(rule);
691
692 let mut engine = BackwardEngine::new(kb);
693 let mut facts = Facts::new();
694 facts.set("User.Score", Value::Number(95.0));
695
696 let result = engine.query("User.HasHighScore == true", &mut facts);
697 assert!(result.is_ok());
698 let query_result = result.unwrap();
699
700 assert!(
702 query_result.provable,
703 "Query should be provable with log action"
704 );
705 }
706}