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
284#[cfg(test)]
285mod tests {
286 use super::*;
287 use crate::KnowledgeBase;
288 use crate::types::Value;
289
290 #[test]
291 fn test_engine_creation() {
292 let kb = KnowledgeBase::new("test");
293 let engine = BackwardEngine::new(kb);
294
295 assert_eq!(engine.config.max_depth, 10);
296 assert_eq!(engine.config.strategy, SearchStrategy::DepthFirst);
297 }
298
299 #[test]
300 fn test_config_customization() {
301 let kb = KnowledgeBase::new("test");
302 let config = BackwardConfig {
303 max_depth: 5,
304 strategy: SearchStrategy::BreadthFirst,
305 enable_memoization: false,
306 max_solutions: 10,
307 };
308
309 let engine = BackwardEngine::with_config(kb, config);
310 assert_eq!(engine.config.max_depth, 5);
311 assert_eq!(engine.config.strategy, SearchStrategy::BreadthFirst);
312 assert!(!engine.config.enable_memoization);
313 }
314
315 #[test]
316 fn test_query_simple() {
317 let kb = KnowledgeBase::new("test");
318 let mut engine = BackwardEngine::new(kb);
319 let mut facts = Facts::new();
320
321 let result = engine.query("User.IsVIP == true", &mut facts);
323 assert!(result.is_ok());
324 }
325
326 #[test]
327 fn test_function_call_condition_len() {
328 use crate::engine::rule::{Rule, Condition, ConditionGroup};
329 use crate::types::{Operator, ActionType};
330
331 let mut kb = KnowledgeBase::new("test");
332
333 let conditions = ConditionGroup::Single(
335 Condition::with_function(
336 "len".to_string(),
337 vec!["User.Name".to_string()],
338 Operator::GreaterThan,
339 Value::Number(3.0),
340 )
341 );
342 let actions = vec![ActionType::Set {
343 field: "User.HasLongName".to_string(),
344 value: Value::Boolean(true),
345 }];
346
347 let rule = Rule::new("CheckNameLength".to_string(), conditions, actions);
348 kb.add_rule(rule);
349
350 let mut engine = BackwardEngine::new(kb);
351 let mut facts = Facts::new();
352 facts.set("User.Name", Value::String("John".to_string()));
353
354 let result = engine.query("User.HasLongName == true", &mut facts);
356 assert!(result.is_ok());
357 let query_result = result.unwrap();
358
359 assert!(query_result.provable, "Query should be provable with len() function");
361 }
362
363 #[test]
364 fn test_function_call_condition_isempty() {
365 use crate::engine::rule::{Rule, Condition, ConditionGroup};
366 use crate::types::{Operator, ActionType};
367
368 let mut kb = KnowledgeBase::new("test");
369
370 let conditions = ConditionGroup::Single(
372 Condition::with_function(
373 "isEmpty".to_string(),
374 vec!["User.Description".to_string()],
375 Operator::Equal,
376 Value::Boolean(false),
377 )
378 );
379 let actions = vec![ActionType::Set {
380 field: "User.HasDescription".to_string(),
381 value: Value::Boolean(true),
382 }];
383
384 let rule = Rule::new("CheckDescription".to_string(), conditions, actions);
385 kb.add_rule(rule);
386
387 let mut engine = BackwardEngine::new(kb);
388 let mut facts = Facts::new();
389 facts.set("User.Description", Value::String("A great user".to_string()));
390
391 let result = engine.query("User.HasDescription == true", &mut facts);
392 assert!(result.is_ok());
393 let query_result = result.unwrap();
394
395 assert!(query_result.provable, "Query should be provable with isEmpty() function");
397 }
398
399 #[test]
400 fn test_test_expression_exists() {
401 use crate::engine::rule::{Rule, Condition, ConditionGroup, ConditionExpression};
402 use crate::types::{Operator, ActionType};
403
404 let mut kb = KnowledgeBase::new("test");
405
406 let condition = Condition {
408 field: "User.Email".to_string(), expression: ConditionExpression::Test {
410 name: "exists".to_string(),
411 args: vec!["User.Email".to_string()],
412 },
413 operator: Operator::Equal,
414 value: Value::Boolean(true),
415 };
416 let conditions = ConditionGroup::Single(condition);
417 let actions = vec![ActionType::Set {
418 field: "User.HasEmail".to_string(),
419 value: Value::Boolean(true),
420 }];
421
422 let rule = Rule::new("CheckEmail".to_string(), conditions, actions);
423 kb.add_rule(rule);
424
425 let mut engine = BackwardEngine::new(kb);
426 let mut facts = Facts::new();
427 facts.set("User.Email", Value::String("user@example.com".to_string()));
428
429 let result = engine.query("User.HasEmail == true", &mut facts);
430 assert!(result.is_ok());
431 let query_result = result.unwrap();
432
433 assert!(query_result.provable, "Query should be provable with exists test");
435 }
436
437 #[test]
438 fn test_multifield_count_operation() {
439 use crate::engine::rule::{Rule, Condition, ConditionGroup, ConditionExpression};
440 use crate::types::{Operator, ActionType};
441
442 let mut kb = KnowledgeBase::new("test");
443
444 let condition = Condition {
446 field: "User.Orders".to_string(), expression: ConditionExpression::MultiField {
448 field: "User.Orders".to_string(),
449 operation: "count".to_string(),
450 variable: None,
451 },
452 operator: Operator::GreaterThan,
453 value: Value::Number(5.0),
454 };
455 let conditions = ConditionGroup::Single(condition);
456 let actions = vec![ActionType::Set {
457 field: "User.IsFrequentBuyer".to_string(),
458 value: Value::Boolean(true),
459 }];
460
461 let rule = Rule::new("CheckFrequentBuyer".to_string(), conditions, actions);
462 kb.add_rule(rule);
463
464 let mut engine = BackwardEngine::new(kb);
465 let mut facts = Facts::new();
466
467 let orders = Value::Array(vec![
469 Value::Number(1.0),
470 Value::Number(2.0),
471 Value::Number(3.0),
472 Value::Number(4.0),
473 Value::Number(5.0),
474 Value::Number(6.0),
475 ]);
476 facts.set("User.Orders", orders);
477
478 let result = engine.query("User.IsFrequentBuyer == true", &mut facts);
479 assert!(result.is_ok());
480 let query_result = result.unwrap();
481
482 assert!(query_result.provable, "Query should be provable with count multifield operation");
484 }
485
486 #[test]
487 fn test_fact_derivation_basic() {
488 use crate::engine::rule::{Rule, Condition, ConditionGroup};
489 use crate::types::{Operator, ActionType};
490
491 let mut kb = KnowledgeBase::new("test");
492
493 let conditions = ConditionGroup::Single(
495 Condition::new(
496 "User.Age".to_string(),
497 Operator::GreaterThan,
498 Value::Number(18.0),
499 )
500 );
501 let actions = vec![ActionType::Set {
502 field: "User.IsAdult".to_string(),
503 value: Value::Boolean(true),
504 }];
505
506 let rule = Rule::new("DetermineAdult".to_string(), conditions, actions);
507 kb.add_rule(rule);
508
509 let mut engine = BackwardEngine::new(kb);
510 let mut facts = Facts::new();
511 facts.set("User.Age", Value::Number(25.0));
512
513 let result = engine.query("User.IsAdult == true", &mut facts);
515 assert!(result.is_ok());
516 let query_result = result.unwrap();
517
518 assert!(query_result.provable, "Fact should be derived by rule action");
519 }
520
521 #[test]
522 fn test_rule_chaining_two_levels() {
523 use crate::engine::rule::{Rule, Condition, ConditionGroup};
524 use crate::types::{Operator, ActionType, LogicalOperator};
525
526 let mut kb = KnowledgeBase::new("test");
527
528 let conditions1 = ConditionGroup::Single(
530 Condition::new(
531 "User.LoyaltyPoints".to_string(),
532 Operator::GreaterThan,
533 Value::Number(100.0),
534 )
535 );
536 let actions1 = vec![ActionType::Set {
537 field: "User.IsVIP".to_string(),
538 value: Value::Boolean(true),
539 }];
540 let rule1 = Rule::new("DetermineVIP".to_string(), conditions1, actions1);
541
542 let conditions2 = ConditionGroup::Compound {
544 left: Box::new(ConditionGroup::Single(
545 Condition::new(
546 "User.IsVIP".to_string(),
547 Operator::Equal,
548 Value::Boolean(true),
549 )
550 )),
551 operator: LogicalOperator::And,
552 right: Box::new(ConditionGroup::Single(
553 Condition::new(
554 "Order.Amount".to_string(),
555 Operator::LessThan,
556 Value::Number(10000.0),
557 )
558 )),
559 };
560 let actions2 = vec![ActionType::Set {
561 field: "Order.AutoApproved".to_string(),
562 value: Value::Boolean(true),
563 }];
564 let rule2 = Rule::new("AutoApproveVIP".to_string(), conditions2, actions2);
565
566 kb.add_rule(rule1);
567 kb.add_rule(rule2);
568
569 let mut engine = BackwardEngine::new(kb);
570 let mut facts = Facts::new();
571 facts.set("User.LoyaltyPoints", Value::Number(150.0));
572 facts.set("Order.Amount", Value::Number(5000.0));
573
574 let result = engine.query("Order.AutoApproved == true", &mut facts);
582 assert!(result.is_ok());
583 let query_result = result.unwrap();
584
585 assert!(query_result.provable, "Query should be provable through 2-level rule chaining");
586 }
587
588 #[test]
589 fn test_fact_derivation_with_log_action() {
590 use crate::engine::rule::{Rule, Condition, ConditionGroup};
591 use crate::types::{Operator, ActionType};
592
593 let mut kb = KnowledgeBase::new("test");
594
595 let conditions = ConditionGroup::Single(
597 Condition::new(
598 "User.Score".to_string(),
599 Operator::GreaterThan,
600 Value::Number(90.0),
601 )
602 );
603 let actions = vec![
604 ActionType::Log {
605 message: "High score achieved!".to_string(),
606 },
607 ActionType::Set {
608 field: "User.HasHighScore".to_string(),
609 value: Value::Boolean(true),
610 }
611 ];
612
613 let rule = Rule::new("HighScoreRule".to_string(), conditions, actions);
614 kb.add_rule(rule);
615
616 let mut engine = BackwardEngine::new(kb);
617 let mut facts = Facts::new();
618 facts.set("User.Score", Value::Number(95.0));
619
620 let result = engine.query("User.HasHighScore == true", &mut facts);
621 assert!(result.is_ok());
622 let query_result = result.unwrap();
623
624 assert!(query_result.provable, "Query should be provable with log action");
626 }
627}