1use std::collections::{HashMap, HashSet};
9use std::sync::Arc;
10use super::working_memory::{WorkingMemory, FactHandle};
11use super::network::{ReteUlNode, TypedReteUlRule};
12use super::facts::{TypedFacts, FactValue};
13use super::agenda::{AdvancedAgenda, Activation};
14use super::template::TemplateRegistry;
15use super::globals::GlobalsRegistry;
16use super::deffacts::DeffactsRegistry;
17use super::tms::TruthMaintenanceSystem;
18use crate::errors::{Result, RuleEngineError};
19
20#[derive(Debug)]
22pub struct RuleDependencyGraph {
23 fact_type_to_rules: HashMap<String, HashSet<usize>>,
25 rule_to_fact_types: HashMap<usize, HashSet<String>>,
27}
28
29impl RuleDependencyGraph {
30 pub fn new() -> Self {
32 Self {
33 fact_type_to_rules: HashMap::new(),
34 rule_to_fact_types: HashMap::new(),
35 }
36 }
37
38 pub fn add_dependency(&mut self, rule_idx: usize, fact_type: String) {
40 self.fact_type_to_rules
41 .entry(fact_type.clone())
42 .or_insert_with(HashSet::new)
43 .insert(rule_idx);
44
45 self.rule_to_fact_types
46 .entry(rule_idx)
47 .or_insert_with(HashSet::new)
48 .insert(fact_type);
49 }
50
51 pub fn get_affected_rules(&self, fact_type: &str) -> HashSet<usize> {
53 self.fact_type_to_rules
54 .get(fact_type)
55 .cloned()
56 .unwrap_or_else(HashSet::new)
57 }
58
59 pub fn get_rule_dependencies(&self, rule_idx: usize) -> HashSet<String> {
61 self.rule_to_fact_types
62 .get(&rule_idx)
63 .cloned()
64 .unwrap_or_else(HashSet::new)
65 }
66}
67
68impl Default for RuleDependencyGraph {
69 fn default() -> Self {
70 Self::new()
71 }
72}
73
74pub type ReteCustomFunction = Arc<dyn Fn(&[FactValue], &TypedFacts) -> Result<FactValue> + Send + Sync>;
77
78pub struct IncrementalEngine {
81 working_memory: WorkingMemory,
83 rules: Vec<TypedReteUlRule>,
85 dependencies: RuleDependencyGraph,
87 agenda: AdvancedAgenda,
89 rule_matched_facts: HashMap<usize, HashSet<FactHandle>>,
91 templates: TemplateRegistry,
93 globals: GlobalsRegistry,
95 deffacts: DeffactsRegistry,
97 custom_functions: HashMap<String, ReteCustomFunction>,
99 tms: TruthMaintenanceSystem,
101}
102
103impl IncrementalEngine {
104 pub fn new() -> Self {
106 Self {
107 working_memory: WorkingMemory::new(),
108 rules: Vec::new(),
109 dependencies: RuleDependencyGraph::new(),
110 agenda: AdvancedAgenda::new(),
111 rule_matched_facts: HashMap::new(),
112 custom_functions: HashMap::new(),
113 templates: TemplateRegistry::new(),
114 globals: GlobalsRegistry::new(),
115 deffacts: DeffactsRegistry::new(),
116 tms: TruthMaintenanceSystem::new(),
117 }
118 }
119
120 pub fn add_rule(&mut self, rule: TypedReteUlRule, depends_on: Vec<String>) {
122 let rule_idx = self.rules.len();
123
124 for fact_type in depends_on {
126 self.dependencies.add_dependency(rule_idx, fact_type);
127 }
128
129 self.rules.push(rule);
130 }
131
132 pub fn insert(&mut self, fact_type: String, data: TypedFacts) -> FactHandle {
134 let handle = self.working_memory.insert(fact_type.clone(), data);
135
136 self.tms.add_explicit_justification(handle);
138
139 self.propagate_changes_for_type(&fact_type);
141
142 handle
143 }
144
145 pub fn update(&mut self, handle: FactHandle, data: TypedFacts) -> Result<()> {
147 let fact_type = self.working_memory
149 .get(&handle)
150 .map(|f| f.fact_type.clone())
151 .ok_or_else(|| RuleEngineError::FieldNotFound {
152 field: format!("FactHandle {} not found", handle),
153 })?;
154
155 self.working_memory.update(handle, data).map_err(|e| RuleEngineError::EvaluationError {
156 message: e,
157 })?;
158
159 self.propagate_changes_for_type(&fact_type);
161
162 Ok(())
163 }
164
165 pub fn retract(&mut self, handle: FactHandle) -> Result<()> {
167 let fact_type = self.working_memory
169 .get(&handle)
170 .map(|f| f.fact_type.clone())
171 .ok_or_else(|| RuleEngineError::FieldNotFound {
172 field: format!("FactHandle {} not found", handle),
173 })?;
174
175 self.working_memory.retract(handle).map_err(|e| RuleEngineError::EvaluationError {
176 message: e,
177 })?;
178
179 let cascaded_facts = self.tms.retract_with_cascade(handle);
181
182 for cascaded_handle in cascaded_facts {
184 if let Ok(fact_type) = self.working_memory
185 .get(&cascaded_handle)
186 .map(|f| f.fact_type.clone())
187 .ok_or_else(|| RuleEngineError::FieldNotFound {
188 field: format!("FactHandle {} not found", cascaded_handle),
189 })
190 {
191 let _ = self.working_memory.retract(cascaded_handle);
192 self.propagate_changes_for_type(&fact_type);
194 }
195 }
196
197 self.propagate_changes_for_type(&fact_type);
199
200 Ok(())
201 }
202
203 pub fn insert_explicit(&mut self, fact_type: String, data: TypedFacts) -> FactHandle {
206 let handle = self.working_memory.insert(fact_type.clone(), data);
207
208 self.tms.add_explicit_justification(handle);
210
211 self.propagate_changes_for_type(&fact_type);
213
214 handle
215 }
216
217 pub fn insert_logical(
226 &mut self,
227 fact_type: String,
228 data: TypedFacts,
229 source_rule: String,
230 premise_handles: Vec<FactHandle>,
231 ) -> FactHandle {
232 let handle = self.working_memory.insert(fact_type.clone(), data);
233
234 self.tms.add_logical_justification(handle, source_rule, premise_handles);
236
237 self.propagate_changes_for_type(&fact_type);
239
240 handle
241 }
242
243 pub fn tms(&self) -> &TruthMaintenanceSystem {
245 &self.tms
246 }
247
248 pub fn tms_mut(&mut self) -> &mut TruthMaintenanceSystem {
250 &mut self.tms
251 }
252
253 fn propagate_changes_for_type(&mut self, fact_type: &str) {
255 let affected_rules = self.dependencies.get_affected_rules(fact_type);
257
258 if affected_rules.is_empty() {
259 return; }
261
262 let facts_of_type = self.working_memory.get_by_type(fact_type);
264
265 for &rule_idx in &affected_rules {
267 let rule = &self.rules[rule_idx];
268
269 for fact in &facts_of_type {
271 let mut single_fact_data = TypedFacts::new();
273 for (key, value) in fact.data.get_all() {
274 single_fact_data.set(format!("{}.{}", fact_type, key), value.clone());
275 }
276 single_fact_data.set_fact_handle(fact_type.to_string(), fact.handle);
278
279 let matches = super::network::evaluate_rete_ul_node_typed(&rule.node, &single_fact_data);
281
282 if matches {
283 let activation = Activation::new(rule.name.clone(), rule.priority)
285 .with_no_loop(rule.no_loop)
286 .with_matched_fact(fact.handle);
287
288 self.agenda.add_activation(activation);
289 }
290 }
291 }
292 }
293
294 fn propagate_changes(&mut self) {
296 let fact_types: Vec<String> = self.working_memory.get_all_facts()
298 .iter()
299 .map(|f| f.fact_type.clone())
300 .collect::<std::collections::HashSet<_>>()
301 .into_iter()
302 .collect();
303
304 for fact_type in fact_types {
306 let facts_of_type = self.working_memory.get_by_type(&fact_type);
307
308 for (rule_idx, rule) in self.rules.iter().enumerate() {
309 if rule.no_loop && self.agenda.has_fired(&rule.name) {
311 continue;
312 }
313
314 for fact in &facts_of_type {
316 let mut single_fact_data = TypedFacts::new();
317 for (key, value) in fact.data.get_all() {
318 single_fact_data.set(format!("{}.{}", fact_type, key), value.clone());
319 }
320
321 let matches = super::network::evaluate_rete_ul_node_typed(&rule.node, &single_fact_data);
322
323 if matches {
324 let activation = Activation::new(rule.name.clone(), rule.priority)
325 .with_no_loop(rule.no_loop)
326 .with_matched_fact(fact.handle);
327
328 self.agenda.add_activation(activation);
329 }
330 }
331 }
332 }
333 }
334
335 pub fn fire_all(&mut self) -> Vec<String> {
337 let mut fired_rules = Vec::new();
338 let max_iterations = 1000; let mut iteration_count = 0;
340
341 while let Some(activation) = self.agenda.get_next_activation() {
342 iteration_count += 1;
343 if iteration_count > max_iterations {
344 eprintln!("WARNING: Maximum iterations ({}) reached in fire_all(). Possible infinite loop!", max_iterations);
345 break;
346 }
347
348 if let Some((idx, rule)) = self.rules
350 .iter_mut()
351 .enumerate()
352 .find(|(_, r)| r.name == activation.rule_name)
353 {
354 if let Some(matched_handle) = activation.matched_fact_handle {
356 if self.working_memory.get(&matched_handle).is_none() {
357 continue;
359 }
360 }
361
362 let original_facts = self.working_memory.to_typed_facts();
364 let mut modified_facts = original_facts.clone();
365
366 if let Some(matched_handle) = activation.matched_fact_handle {
368 if let Some(fact) = self.working_memory.get(&matched_handle) {
370 modified_facts.set_fact_handle(fact.fact_type.clone(), matched_handle);
371 }
372 }
373
374 let mut action_results = super::ActionResults::new();
375 (rule.action)(&mut modified_facts, &mut action_results);
376
377 let mut updates_by_type: HashMap<String, Vec<(String, FactValue)>> = HashMap::new();
380
381 for (key, value) in modified_facts.get_all() {
382 if let Some(original_value) = original_facts.get(key) {
385 if original_value != value {
386 let parts: Vec<&str> = key.split('.').collect();
388 if parts.len() >= 2 {
389 let fact_type = parts[0].to_string();
390 let field = if parts.len() == 2 {
392 parts[1].to_string()
393 } else {
394 parts[parts.len() - 1].to_string()
395 };
396
397 updates_by_type
398 .entry(fact_type)
399 .or_insert_with(Vec::new)
400 .push((field, value.clone()));
401 }
402 }
403 } else {
404 let parts: Vec<&str> = key.split('.').collect();
406 if parts.len() >= 2 {
407 let fact_type = parts[0].to_string();
408 let field = if parts.len() == 2 {
409 parts[1].to_string()
410 } else {
411 parts[parts.len() - 1].to_string()
412 };
413
414 updates_by_type
415 .entry(fact_type)
416 .or_insert_with(Vec::new)
417 .push((field, value.clone()));
418 }
419 }
420 }
421
422 for (fact_type, field_updates) in updates_by_type {
424 let fact_handles: Vec<FactHandle> = self.working_memory
426 .get_by_type(&fact_type)
427 .iter()
428 .map(|f| f.handle)
429 .collect();
430
431 for handle in fact_handles {
432 if let Some(fact) = self.working_memory.get(&handle) {
433 let mut updated_data = fact.data.clone();
434
435 for (field, value) in &field_updates {
437 updated_data.set(field, value.clone());
438 }
439
440 let _ = self.working_memory.update(handle, updated_data);
441 }
442 }
443 }
444
445 self.propagate_changes();
448
449 self.process_action_results(action_results);
451
452 fired_rules.push(activation.rule_name.clone());
454 self.agenda.mark_rule_fired(&activation);
455 }
456 }
457
458 fired_rules
459 }
460
461 fn process_action_results(&mut self, results: super::ActionResults) {
463 for result in results.results {
464 match result {
465 super::ActionResult::Retract(handle) => {
466 if let Err(e) = self.retract(handle) {
468 eprintln!("❌ Failed to retract fact {:?}: {}", handle, e);
469 }
470 }
471 super::ActionResult::RetractByType(fact_type) => {
472 let facts_of_type = self.working_memory.get_by_type(&fact_type);
474 if let Some(fact) = facts_of_type.first() {
475 let handle = fact.handle;
476 if let Err(e) = self.retract(handle) {
477 eprintln!("❌ Failed to retract fact {:?}: {}", handle, e);
478 }
479 }
480 }
481 super::ActionResult::Update(handle) => {
482 if let Some(fact) = self.working_memory.get(&handle) {
484 let fact_type = fact.fact_type.clone();
485 self.propagate_changes_for_type(&fact_type);
486 }
487 }
488 super::ActionResult::ActivateAgendaGroup(group) => {
489 self.agenda.set_focus(group);
491 }
492 super::ActionResult::InsertFact { fact_type, data } => {
493 self.insert_explicit(fact_type, data);
495 }
496 super::ActionResult::InsertLogicalFact { fact_type, data, rule_name, premises } => {
497 let _handle = self.insert_logical(fact_type, data, rule_name, premises);
499 }
500 super::ActionResult::CallFunction { function_name, args } => {
501 println!("🔧 Function call queued: {}({:?})", function_name, args);
503 }
505 super::ActionResult::ScheduleRule { rule_name, delay_ms } => {
506 println!("⏰ Rule scheduled: {} after {}ms", rule_name, delay_ms);
508 }
510 super::ActionResult::None => {
511 }
513 }
514 }
515 }
516
517 pub fn working_memory(&self) -> &WorkingMemory {
519 &self.working_memory
520 }
521
522 pub fn working_memory_mut(&mut self) -> &mut WorkingMemory {
524 &mut self.working_memory
525 }
526
527 pub fn agenda(&self) -> &AdvancedAgenda {
529 &self.agenda
530 }
531
532 pub fn agenda_mut(&mut self) -> &mut AdvancedAgenda {
534 &mut self.agenda
535 }
536
537 pub fn set_conflict_resolution_strategy(
542 &mut self,
543 strategy: super::agenda::ConflictResolutionStrategy,
544 ) {
545 self.agenda.set_strategy(strategy);
546 }
547
548 pub fn conflict_resolution_strategy(&self) -> super::agenda::ConflictResolutionStrategy {
550 self.agenda.strategy()
551 }
552
553 pub fn stats(&self) -> IncrementalEngineStats {
555 IncrementalEngineStats {
556 rules: self.rules.len(),
557 working_memory: self.working_memory.stats(),
558 agenda: self.agenda.stats(),
559 dependencies: self.dependencies.fact_type_to_rules.len(),
560 }
561 }
562
563 pub fn reset(&mut self) {
565 self.agenda.reset_fired_flags();
566 }
567
568 pub fn templates(&self) -> &TemplateRegistry {
570 &self.templates
571 }
572
573 pub fn templates_mut(&mut self) -> &mut TemplateRegistry {
575 &mut self.templates
576 }
577
578 pub fn register_function<F>(&mut self, name: &str, func: F)
597 where
598 F: Fn(&[FactValue], &TypedFacts) -> Result<FactValue> + Send + Sync + 'static,
599 {
600 self.custom_functions.insert(name.to_string(), Arc::new(func));
601 }
602
603 pub fn get_function(&self, name: &str) -> Option<&ReteCustomFunction> {
605 self.custom_functions.get(name)
606 }
607
608 pub fn globals(&self) -> &GlobalsRegistry {
610 &self.globals
611 }
612
613 pub fn globals_mut(&mut self) -> &mut GlobalsRegistry {
615 &mut self.globals
616 }
617
618 pub fn deffacts(&self) -> &DeffactsRegistry {
620 &self.deffacts
621 }
622
623 pub fn deffacts_mut(&mut self) -> &mut DeffactsRegistry {
625 &mut self.deffacts
626 }
627
628 pub fn load_deffacts(&mut self) -> Vec<FactHandle> {
631 let mut handles = Vec::new();
632
633 let all_facts = self.deffacts.get_all_facts();
635
636 for (_deffacts_name, fact_instance) in all_facts {
637 let handle = if self.templates.get(&fact_instance.fact_type).is_some() {
639 match self.insert_with_template(&fact_instance.fact_type, fact_instance.data) {
641 Ok(h) => h,
642 Err(_) => continue, }
644 } else {
645 self.insert(fact_instance.fact_type, fact_instance.data)
647 };
648
649 handles.push(handle);
650 }
651
652 handles
653 }
654
655 pub fn load_deffacts_by_name(&mut self, name: &str) -> crate::errors::Result<Vec<FactHandle>> {
658 let facts_to_insert = {
660 let deffacts = self.deffacts.get(name).ok_or_else(|| {
661 crate::errors::RuleEngineError::EvaluationError {
662 message: format!("Deffacts '{}' not found", name),
663 }
664 })?;
665 deffacts.facts.clone()
666 };
667
668 let mut handles = Vec::new();
669
670 for fact_instance in facts_to_insert {
671 let handle = if self.templates.get(&fact_instance.fact_type).is_some() {
673 self.insert_with_template(&fact_instance.fact_type, fact_instance.data)?
675 } else {
676 self.insert(fact_instance.fact_type, fact_instance.data)
678 };
679
680 handles.push(handle);
681 }
682
683 Ok(handles)
684 }
685
686 pub fn reset_with_deffacts(&mut self) -> Vec<FactHandle> {
689 self.working_memory = WorkingMemory::new();
691 self.agenda.clear();
692 self.rule_matched_facts.clear();
693
694 self.load_deffacts()
696 }
697
698 pub fn insert_with_template(
700 &mut self,
701 template_name: &str,
702 data: TypedFacts,
703 ) -> crate::errors::Result<FactHandle> {
704 self.templates.validate(template_name, &data)?;
706
707 Ok(self.insert(template_name.to_string(), data))
709 }
710}
711
712impl Default for IncrementalEngine {
713 fn default() -> Self {
714 Self::new()
715 }
716}
717
718#[derive(Debug)]
720pub struct IncrementalEngineStats {
721 pub rules: usize,
722 pub working_memory: super::working_memory::WorkingMemoryStats,
723 pub agenda: super::agenda::AgendaStats,
724 pub dependencies: usize,
725}
726
727impl std::fmt::Display for IncrementalEngineStats {
728 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
729 write!(
730 f,
731 "Engine Stats: {} rules, {} fact types tracked\nWM: {}\nAgenda: {}",
732 self.rules,
733 self.dependencies,
734 self.working_memory,
735 self.agenda
736 )
737 }
738}
739
740#[cfg(test)]
741mod tests {
742 use super::*;
743 use crate::rete::network::ReteUlNode;
744 use crate::rete::alpha::AlphaNode;
745
746 #[test]
747 fn test_dependency_graph() {
748 let mut graph = RuleDependencyGraph::new();
749
750 graph.add_dependency(0, "Person".to_string());
751 graph.add_dependency(1, "Person".to_string());
752 graph.add_dependency(1, "Order".to_string());
753
754 let affected = graph.get_affected_rules("Person");
755 assert_eq!(affected.len(), 2);
756 assert!(affected.contains(&0));
757 assert!(affected.contains(&1));
758
759 let deps = graph.get_rule_dependencies(1);
760 assert_eq!(deps.len(), 2);
761 assert!(deps.contains("Person"));
762 assert!(deps.contains("Order"));
763 }
764
765 #[test]
766 fn test_incremental_propagation() {
767 let mut engine = IncrementalEngine::new();
768
769 let node = ReteUlNode::UlAlpha(AlphaNode {
771 field: "Person.age".to_string(),
772 operator: ">".to_string(),
773 value: "18".to_string(),
774 });
775
776 let rule = TypedReteUlRule {
777 name: "IsAdult".to_string(),
778 node,
779 priority: 0,
780 no_loop: true,
781 action: std::sync::Arc::new(|_, _| {}),
782 };
783
784 engine.add_rule(rule, vec!["Person".to_string()]);
785
786 let mut person = TypedFacts::new();
788 person.set("age", 25i64);
789 let handle = engine.insert("Person".to_string(), person);
790
791 let stats = engine.stats();
793 assert!(stats.agenda.total_activations > 0);
794
795 let mut updated = TypedFacts::new();
797 updated.set("age", 15i64); engine.update(handle, updated).unwrap();
799
800 }
802}