1use super::goal::{Goal, GoalManager, GoalStatus};
4use super::search::{DepthFirstSearch, SearchStrategy, SearchResult};
5use super::query::{QueryParser, QueryResult, QueryStats, ProofTrace};
6use crate::{Facts, KnowledgeBase};
7use crate::errors::Result;
8use std::sync::Arc;
9
10#[derive(Debug, Clone)]
12pub struct BackwardConfig {
13 pub max_depth: usize,
15
16 pub strategy: SearchStrategy,
18
19 pub enable_memoization: bool,
21
22 pub max_solutions: usize,
24}
25
26impl Default for BackwardConfig {
27 fn default() -> Self {
28 Self {
29 max_depth: 10,
30 strategy: SearchStrategy::DepthFirst,
31 enable_memoization: true,
32 max_solutions: 1,
33 }
34 }
35}
36
37pub struct BackwardEngine {
39 knowledge_base: Arc<KnowledgeBase>,
40 config: BackwardConfig,
41 goal_manager: GoalManager,
42}
43
44impl BackwardEngine {
45 pub fn new(kb: KnowledgeBase) -> Self {
47 Self {
48 knowledge_base: Arc::new(kb),
49 config: BackwardConfig::default(),
50 goal_manager: GoalManager::default(),
51 }
52 }
53
54 pub fn with_config(kb: KnowledgeBase, config: BackwardConfig) -> Self {
56 Self {
57 knowledge_base: Arc::new(kb),
58 goal_manager: GoalManager::new(config.max_depth),
59 config,
60 }
61 }
62
63 pub fn set_config(&mut self, config: BackwardConfig) {
65 self.goal_manager = GoalManager::new(config.max_depth);
66 self.config = config;
67 }
68
69 pub fn query(&mut self, query_str: &str, facts: &Facts) -> Result<QueryResult> {
82 let mut goal = QueryParser::parse(query_str)
84 .map_err(|e| crate::errors::RuleEngineError::ParseError { message: e })?;
85
86 if self.config.enable_memoization {
88 if let Some(cached) = self.goal_manager.is_cached(query_str) {
89 return Ok(if cached {
90 QueryResult::success(
91 goal.bindings.to_map(), ProofTrace::from_goal(&goal),
93 QueryStats::default(),
94 )
95 } else {
96 QueryResult::failure(vec![], QueryStats::default())
97 });
98 }
99 }
100
101 self.find_candidate_rules(&mut goal)?;
103
104 let search_result = self.execute_search(&mut goal, facts)?;
106
107 if self.config.enable_memoization {
109 self.goal_manager.cache_result(query_str.to_string(), search_result.success);
110 }
111
112 let stats = QueryStats {
114 goals_explored: search_result.goals_explored,
115 rules_evaluated: search_result.path.len(),
116 max_depth: search_result.max_depth_reached,
117 duration_ms: None,
118 };
119
120 Ok(if search_result.success {
121 QueryResult::success(
122 search_result.bindings,
123 ProofTrace::from_goal(&goal),
124 stats,
125 )
126 } else {
127 QueryResult::failure(self.find_missing_facts(&goal), stats)
128 })
129 }
130
131 fn find_candidate_rules(&self, goal: &mut Goal) -> Result<()> {
133 for rule in self.knowledge_base.get_rules() {
140 if self.rule_could_prove_goal(&rule, goal) {
142 goal.add_candidate_rule(rule.name.clone());
143 }
144 }
145
146 Ok(())
147 }
148
149 fn rule_could_prove_goal(&self, rule: &crate::engine::rule::Rule, goal: &Goal) -> bool {
151 if goal.pattern.contains(&rule.name) {
156 return true;
157 }
158
159 for action in &rule.actions {
161 match action {
162 crate::types::ActionType::Set { field, .. } => {
163 if goal.pattern.contains(field) {
164 return true;
165 }
166 }
167 crate::types::ActionType::MethodCall { object, method, .. } => {
168 if goal.pattern.contains(object) || goal.pattern.contains(method) {
169 return true;
170 }
171 }
172 _ => {}
173 }
174 }
175
176 false
177 }
178
179 fn execute_search(&mut self, goal: &mut Goal, facts: &Facts) -> Result<SearchResult> {
181 match self.config.strategy {
182 SearchStrategy::DepthFirst => {
183 let mut dfs = DepthFirstSearch::new(self.config.max_depth, (*self.knowledge_base).clone());
184 Ok(dfs.search_with_execution(goal, facts, &self.knowledge_base))
186 }
187 SearchStrategy::BreadthFirst => {
188 let mut dfs = DepthFirstSearch::new(self.config.max_depth, (*self.knowledge_base).clone());
190 Ok(dfs.search_with_execution(goal, facts, &self.knowledge_base))
191 }
192 SearchStrategy::Iterative => {
193 let mut dfs = DepthFirstSearch::new(self.config.max_depth, (*self.knowledge_base).clone());
195 Ok(dfs.search_with_execution(goal, facts, &self.knowledge_base))
196 }
197 }
198 }
199
200 fn find_missing_facts(&self, goal: &Goal) -> Vec<String> {
202 let mut missing = Vec::new();
203
204 self.collect_missing_recursive(goal, &mut missing);
206
207 missing
208 }
209
210 fn collect_missing_recursive(&self, goal: &Goal, missing: &mut Vec<String>) {
211 if goal.status == GoalStatus::Unprovable {
212 missing.push(goal.pattern.clone());
213 }
214
215 for sub_goal in &goal.sub_goals {
216 self.collect_missing_recursive(sub_goal, missing);
217 }
218 }
219
220 pub fn explain_why(&mut self, query_str: &str, facts: &Facts) -> Result<String> {
222 let result = self.query(query_str, facts)?;
223
224 if result.provable {
225 Ok(format!(
226 "Goal '{}' is PROVABLE\nProof trace:\n{:#?}",
227 query_str, result.proof_trace
228 ))
229 } else {
230 Ok(format!(
231 "Goal '{}' is NOT provable\nMissing facts: {:?}",
232 query_str, result.missing_facts
233 ))
234 }
235 }
236
237 pub fn config(&self) -> &BackwardConfig {
239 &self.config
240 }
241
242 pub fn knowledge_base(&self) -> &KnowledgeBase {
244 &self.knowledge_base
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use crate::KnowledgeBase;
252 use crate::types::Value;
253
254 #[test]
255 fn test_engine_creation() {
256 let kb = KnowledgeBase::new("test");
257 let engine = BackwardEngine::new(kb);
258
259 assert_eq!(engine.config.max_depth, 10);
260 assert_eq!(engine.config.strategy, SearchStrategy::DepthFirst);
261 }
262
263 #[test]
264 fn test_config_customization() {
265 let kb = KnowledgeBase::new("test");
266 let config = BackwardConfig {
267 max_depth: 5,
268 strategy: SearchStrategy::BreadthFirst,
269 enable_memoization: false,
270 max_solutions: 10,
271 };
272
273 let engine = BackwardEngine::with_config(kb, config);
274 assert_eq!(engine.config.max_depth, 5);
275 assert_eq!(engine.config.strategy, SearchStrategy::BreadthFirst);
276 assert!(!engine.config.enable_memoization);
277 }
278
279 #[test]
280 fn test_query_simple() {
281 let kb = KnowledgeBase::new("test");
282 let mut engine = BackwardEngine::new(kb);
283 let facts = Facts::new();
284
285 let result = engine.query("User.IsVIP == true", &facts);
287 assert!(result.is_ok());
288 }
289
290 #[test]
291 fn test_function_call_condition_len() {
292 use crate::engine::rule::{Rule, Condition, ConditionGroup};
293 use crate::types::{Operator, ActionType};
294
295 let mut kb = KnowledgeBase::new("test");
296
297 let conditions = ConditionGroup::Single(
299 Condition::with_function(
300 "len".to_string(),
301 vec!["User.Name".to_string()],
302 Operator::GreaterThan,
303 Value::Number(3.0),
304 )
305 );
306 let actions = vec![ActionType::Set {
307 field: "User.HasLongName".to_string(),
308 value: Value::Boolean(true),
309 }];
310
311 let rule = Rule::new("CheckNameLength".to_string(), conditions, actions);
312 kb.add_rule(rule);
313
314 let mut engine = BackwardEngine::new(kb);
315 let mut facts = Facts::new();
316 facts.set("User.Name", Value::String("John".to_string()));
317
318 let result = engine.query("User.HasLongName == true", &facts);
320 assert!(result.is_ok());
321 let query_result = result.unwrap();
322
323 assert!(query_result.provable, "Query should be provable with len() function");
325 }
326
327 #[test]
328 fn test_function_call_condition_isempty() {
329 use crate::engine::rule::{Rule, Condition, ConditionGroup};
330 use crate::types::{Operator, ActionType};
331
332 let mut kb = KnowledgeBase::new("test");
333
334 let conditions = ConditionGroup::Single(
336 Condition::with_function(
337 "isEmpty".to_string(),
338 vec!["User.Description".to_string()],
339 Operator::Equal,
340 Value::Boolean(false),
341 )
342 );
343 let actions = vec![ActionType::Set {
344 field: "User.HasDescription".to_string(),
345 value: Value::Boolean(true),
346 }];
347
348 let rule = Rule::new("CheckDescription".to_string(), conditions, actions);
349 kb.add_rule(rule);
350
351 let mut engine = BackwardEngine::new(kb);
352 let mut facts = Facts::new();
353 facts.set("User.Description", Value::String("A great user".to_string()));
354
355 let result = engine.query("User.HasDescription == true", &facts);
356 assert!(result.is_ok());
357 let query_result = result.unwrap();
358
359 assert!(query_result.provable, "Query should be provable with isEmpty() function");
361 }
362
363 #[test]
364 fn test_test_expression_exists() {
365 use crate::engine::rule::{Rule, Condition, ConditionGroup, ConditionExpression};
366 use crate::types::{Operator, ActionType};
367
368 let mut kb = KnowledgeBase::new("test");
369
370 let condition = Condition {
372 field: "User.Email".to_string(), expression: ConditionExpression::Test {
374 name: "exists".to_string(),
375 args: vec!["User.Email".to_string()],
376 },
377 operator: Operator::Equal,
378 value: Value::Boolean(true),
379 };
380 let conditions = ConditionGroup::Single(condition);
381 let actions = vec![ActionType::Set {
382 field: "User.HasEmail".to_string(),
383 value: Value::Boolean(true),
384 }];
385
386 let rule = Rule::new("CheckEmail".to_string(), conditions, actions);
387 kb.add_rule(rule);
388
389 let mut engine = BackwardEngine::new(kb);
390 let mut facts = Facts::new();
391 facts.set("User.Email", Value::String("user@example.com".to_string()));
392
393 let result = engine.query("User.HasEmail == true", &facts);
394 assert!(result.is_ok());
395 let query_result = result.unwrap();
396
397 assert!(query_result.provable, "Query should be provable with exists test");
399 }
400
401 #[test]
402 fn test_multifield_count_operation() {
403 use crate::engine::rule::{Rule, Condition, ConditionGroup, ConditionExpression};
404 use crate::types::{Operator, ActionType};
405
406 let mut kb = KnowledgeBase::new("test");
407
408 let condition = Condition {
410 field: "User.Orders".to_string(), expression: ConditionExpression::MultiField {
412 field: "User.Orders".to_string(),
413 operation: "count".to_string(),
414 variable: None,
415 },
416 operator: Operator::GreaterThan,
417 value: Value::Number(5.0),
418 };
419 let conditions = ConditionGroup::Single(condition);
420 let actions = vec![ActionType::Set {
421 field: "User.IsFrequentBuyer".to_string(),
422 value: Value::Boolean(true),
423 }];
424
425 let rule = Rule::new("CheckFrequentBuyer".to_string(), conditions, actions);
426 kb.add_rule(rule);
427
428 let mut engine = BackwardEngine::new(kb);
429 let mut facts = Facts::new();
430
431 let orders = Value::Array(vec![
433 Value::Number(1.0),
434 Value::Number(2.0),
435 Value::Number(3.0),
436 Value::Number(4.0),
437 Value::Number(5.0),
438 Value::Number(6.0),
439 ]);
440 facts.set("User.Orders", orders);
441
442 let result = engine.query("User.IsFrequentBuyer == true", &facts);
443 assert!(result.is_ok());
444 let query_result = result.unwrap();
445
446 assert!(query_result.provable, "Query should be provable with count multifield operation");
448 }
449
450 #[test]
451 fn test_fact_derivation_basic() {
452 use crate::engine::rule::{Rule, Condition, ConditionGroup};
453 use crate::types::{Operator, ActionType};
454
455 let mut kb = KnowledgeBase::new("test");
456
457 let conditions = ConditionGroup::Single(
459 Condition::new(
460 "User.Age".to_string(),
461 Operator::GreaterThan,
462 Value::Number(18.0),
463 )
464 );
465 let actions = vec![ActionType::Set {
466 field: "User.IsAdult".to_string(),
467 value: Value::Boolean(true),
468 }];
469
470 let rule = Rule::new("DetermineAdult".to_string(), conditions, actions);
471 kb.add_rule(rule);
472
473 let mut engine = BackwardEngine::new(kb);
474 let mut facts = Facts::new();
475 facts.set("User.Age", Value::Number(25.0));
476
477 let result = engine.query("User.IsAdult == true", &facts);
479 assert!(result.is_ok());
480 let query_result = result.unwrap();
481
482 assert!(query_result.provable, "Fact should be derived by rule action");
483 }
484
485 #[test]
486 fn test_rule_chaining_two_levels() {
487 use crate::engine::rule::{Rule, Condition, ConditionGroup};
488 use crate::types::{Operator, ActionType, LogicalOperator};
489
490 let mut kb = KnowledgeBase::new("test");
491
492 let conditions1 = ConditionGroup::Single(
494 Condition::new(
495 "User.LoyaltyPoints".to_string(),
496 Operator::GreaterThan,
497 Value::Number(100.0),
498 )
499 );
500 let actions1 = vec![ActionType::Set {
501 field: "User.IsVIP".to_string(),
502 value: Value::Boolean(true),
503 }];
504 let rule1 = Rule::new("DetermineVIP".to_string(), conditions1, actions1);
505
506 let conditions2 = ConditionGroup::Compound {
508 left: Box::new(ConditionGroup::Single(
509 Condition::new(
510 "User.IsVIP".to_string(),
511 Operator::Equal,
512 Value::Boolean(true),
513 )
514 )),
515 operator: LogicalOperator::And,
516 right: Box::new(ConditionGroup::Single(
517 Condition::new(
518 "Order.Amount".to_string(),
519 Operator::LessThan,
520 Value::Number(10000.0),
521 )
522 )),
523 };
524 let actions2 = vec![ActionType::Set {
525 field: "Order.AutoApproved".to_string(),
526 value: Value::Boolean(true),
527 }];
528 let rule2 = Rule::new("AutoApproveVIP".to_string(), conditions2, actions2);
529
530 kb.add_rule(rule1);
531 kb.add_rule(rule2);
532
533 let mut engine = BackwardEngine::new(kb);
534 let mut facts = Facts::new();
535 facts.set("User.LoyaltyPoints", Value::Number(150.0));
536 facts.set("Order.Amount", Value::Number(5000.0));
537
538 let result = engine.query("Order.AutoApproved == true", &facts);
546 assert!(result.is_ok());
547 let query_result = result.unwrap();
548
549 assert!(query_result.provable, "Query should be provable through 2-level rule chaining");
550 }
551
552 #[test]
553 fn test_fact_derivation_with_log_action() {
554 use crate::engine::rule::{Rule, Condition, ConditionGroup};
555 use crate::types::{Operator, ActionType};
556
557 let mut kb = KnowledgeBase::new("test");
558
559 let conditions = ConditionGroup::Single(
561 Condition::new(
562 "User.Score".to_string(),
563 Operator::GreaterThan,
564 Value::Number(90.0),
565 )
566 );
567 let actions = vec![
568 ActionType::Log {
569 message: "High score achieved!".to_string(),
570 },
571 ActionType::Set {
572 field: "User.HasHighScore".to_string(),
573 value: Value::Boolean(true),
574 }
575 ];
576
577 let rule = Rule::new("HighScoreRule".to_string(), conditions, actions);
578 kb.add_rule(rule);
579
580 let mut engine = BackwardEngine::new(kb);
581 let mut facts = Facts::new();
582 facts.set("User.Score", Value::Number(95.0));
583
584 let result = engine.query("User.HasHighScore == true", &facts);
585 assert!(result.is_ok());
586 let query_result = result.unwrap();
587
588 assert!(query_result.provable, "Query should be provable with log action");
590 }
591}