1use crate::ai::AiConfig;
7use anyhow::Result;
8use scirs2_core::random::{Random, RngExt};
9use serde::{Deserialize, Serialize};
10use std::collections::{BTreeMap, HashMap};
11use std::time::{SystemTime, UNIX_EPOCH};
12
13pub struct TemporalReasoner {
15 config: TemporalConfig,
17
18 temporal_kb: TemporalKnowledgeBase,
20
21 inference_engine: Box<dyn TemporalInferenceEngine>,
23
24 event_detector: Box<dyn EventDetector>,
26
27 #[allow(dead_code)]
29 constraint_solver: Box<dyn TemporalConstraintSolver>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct TemporalConfig {
35 pub enable_inference: bool,
37
38 pub enable_event_detection: bool,
40
41 pub temporal_resolution: TemporalResolution,
43
44 pub max_inference_depth: usize,
46
47 pub inference_confidence_threshold: f32,
49
50 pub enable_constraint_solving: bool,
52
53 pub supported_relations: Vec<TemporalRelation>,
55}
56
57impl Default for TemporalConfig {
58 fn default() -> Self {
59 Self {
60 enable_inference: true,
61 enable_event_detection: true,
62 temporal_resolution: TemporalResolution::Day,
63 max_inference_depth: 5,
64 inference_confidence_threshold: 0.7,
65 enable_constraint_solving: true,
66 supported_relations: vec![
67 TemporalRelation::Before,
68 TemporalRelation::After,
69 TemporalRelation::During,
70 TemporalRelation::Overlaps,
71 TemporalRelation::Meets,
72 TemporalRelation::Starts,
73 TemporalRelation::Finishes,
74 TemporalRelation::Equals,
75 ],
76 }
77 }
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub enum TemporalResolution {
83 Millisecond,
84 Second,
85 Minute,
86 Hour,
87 Day,
88 Week,
89 Month,
90 Year,
91 Decade,
92 Century,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
97pub enum TemporalRelation {
98 Before,
99 After,
100 During,
101 Contains,
102 Overlaps,
103 OverlappedBy,
104 Meets,
105 MetBy,
106 Starts,
107 StartedBy,
108 Finishes,
109 FinishedBy,
110 Equals,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct TemporalQuery {
116 pub query_type: TemporalQueryType,
118
119 pub entities: Vec<String>,
121
122 pub constraints: Vec<TemporalConstraint>,
124
125 pub time_window: Option<TimeInterval>,
127
128 pub include_inferred: bool,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub enum TemporalQueryType {
135 ValidAt { time: Timestamp },
137
138 ValidDuring { interval: TimeInterval },
140
141 TemporalRelations { entity1: String, entity2: String },
143
144 EventSequence { pattern: Vec<EventPattern> },
146
147 Aggregation {
149 function: AggregationFunction,
150 grouping: TemporalGrouping,
151 },
152
153 ChangeDetection { entity: String, property: String },
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct TemporalResult {
160 pub query_id: String,
162
163 pub results: Vec<TemporalFact>,
165
166 pub inference_trace: Option<Vec<InferenceStep>>,
168
169 pub execution_time: std::time::Duration,
171
172 pub confidence: f32,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct TemporalFact {
179 pub subject: String,
181
182 pub predicate: String,
184
185 pub object: String,
187
188 pub validity: TimeInterval,
190
191 pub confidence: f32,
193
194 pub source: FactSource,
196
197 pub annotations: HashMap<String, String>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203pub enum FactSource {
204 Asserted,
206
207 Inferred { rule: String, premises: Vec<String> },
209
210 TemporalInference { reasoning_type: String },
212
213 EventDetection { detector: String },
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct TimeInterval {
220 pub start: Timestamp,
222
223 pub end: Timestamp,
225
226 pub interval_type: IntervalType,
228}
229
230impl TimeInterval {
231 pub fn contains(&self, timestamp: Timestamp) -> bool {
233 match self.interval_type {
234 IntervalType::Closed => timestamp >= self.start && timestamp <= self.end,
235 IntervalType::Open => timestamp > self.start && timestamp < self.end,
236 IntervalType::LeftOpen => timestamp > self.start && timestamp <= self.end,
237 IntervalType::RightOpen => timestamp >= self.start && timestamp < self.end,
238 }
239 }
240
241 pub fn overlaps(&self, other: &TimeInterval) -> bool {
243 self.start < other.end && other.start < self.end
244 }
245
246 pub fn relation_to(&self, other: &TimeInterval) -> TemporalRelation {
248 if self.end < other.start {
249 TemporalRelation::Before
250 } else if self.start > other.end {
251 TemporalRelation::After
252 } else if self.start == other.start && self.end == other.end {
253 TemporalRelation::Equals
254 } else if self.start >= other.start && self.end <= other.end {
255 TemporalRelation::During
256 } else if self.start <= other.start && self.end >= other.end {
257 TemporalRelation::Contains
258 } else if self.end == other.start {
259 TemporalRelation::Meets
260 } else if self.start == other.end {
261 TemporalRelation::MetBy
262 } else if self.start == other.start && self.end < other.end {
263 TemporalRelation::Starts
264 } else if self.start == other.start && self.end > other.end {
265 TemporalRelation::StartedBy
266 } else if self.end == other.end && self.start > other.start {
267 TemporalRelation::Finishes
268 } else if self.end == other.end && self.start < other.start {
269 TemporalRelation::FinishedBy
270 } else if self.overlaps(other) && self.start < other.start {
271 TemporalRelation::Overlaps
272 } else {
273 TemporalRelation::OverlappedBy
274 }
275 }
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
280pub enum IntervalType {
281 Closed,
283
284 Open,
286
287 LeftOpen,
289
290 RightOpen,
292}
293
294pub type Timestamp = u64;
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct TemporalConstraint {
300 pub constraint_type: ConstraintType,
302
303 pub entity: String,
305
306 pub relation: TemporalRelation,
308
309 pub reference: TemporalReference,
311
312 pub strength: ConstraintStrength,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
318pub enum ConstraintType {
319 Hard,
321
322 Soft { weight: f32 },
324
325 Conditional { condition: String },
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
331pub enum TemporalReference {
332 Absolute(Timestamp),
334
335 Interval(TimeInterval),
337
338 Relative { entity: String, offset: Option<i64> },
340
341 Now,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
347pub enum ConstraintStrength {
348 Required,
349 Strong,
350 Medium,
351 Weak,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct EventPattern {
357 pub event_type: String,
359
360 pub entities: Vec<String>,
362
363 pub constraints: Vec<TemporalConstraint>,
365
366 pub optional: bool,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372pub enum AggregationFunction {
373 Count,
374 Sum,
375 Average,
376 Min,
377 Max,
378 Duration,
379 Frequency,
380}
381
382#[derive(Debug, Clone, Serialize, Deserialize)]
384pub enum TemporalGrouping {
385 ByHour,
386 ByDay,
387 ByWeek,
388 ByMonth,
389 ByYear,
390 ByInterval { duration: u64 },
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct InferenceStep {
396 pub step: usize,
398
399 pub rule: String,
401
402 pub inputs: Vec<TemporalFact>,
404
405 pub output: TemporalFact,
407
408 pub confidence: f32,
410}
411
412pub struct TemporalKnowledgeBase {
414 facts_by_time: BTreeMap<Timestamp, Vec<TemporalFact>>,
416
417 facts_by_entity: HashMap<String, Vec<TemporalFact>>,
419
420 #[allow(dead_code)]
422 temporal_rules: Vec<TemporalRule>,
423
424 event_definitions: HashMap<String, EventDefinition>,
426}
427
428#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct TemporalRule {
431 pub id: String,
433
434 pub name: String,
436
437 pub premises: Vec<TemporalPattern>,
439
440 pub conclusion: TemporalPattern,
442
443 pub confidence: f32,
445
446 pub temporal_constraints: Vec<TemporalConstraint>,
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize)]
452pub struct TemporalPattern {
453 pub variables: HashMap<String, String>,
455
456 pub temporal_conditions: Vec<TemporalCondition>,
458
459 pub confidence: f32,
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct TemporalCondition {
466 pub subject: String,
468
469 pub predicate: String,
471
472 pub object: String,
474
475 pub validity: TemporalValidity,
477}
478
479#[derive(Debug, Clone, Serialize, Deserialize)]
481pub enum TemporalValidity {
482 Always,
484
485 During(TimeInterval),
487
488 At(Timestamp),
490
491 Relative {
493 reference: String,
494 relation: TemporalRelation,
495 },
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize)]
500pub struct EventDefinition {
501 pub event_type: String,
503
504 pub patterns: Vec<EventDetectionPattern>,
506
507 pub duration_constraints: Option<TimeInterval>,
509
510 pub participants: Vec<ParticipantRole>,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
516pub struct EventDetectionPattern {
517 pub conditions: Vec<TemporalCondition>,
519
520 pub ordering: Vec<TemporalOrdering>,
522
523 pub confidence: f32,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct TemporalOrdering {
530 pub first: String,
532
533 pub second: String,
535
536 pub relation: TemporalRelation,
538
539 pub bounds: Option<TimeInterval>,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct ParticipantRole {
546 pub role: String,
548
549 pub entity_type: String,
551
552 pub required: bool,
554}
555
556pub trait TemporalInferenceEngine: Send + Sync {
558 fn infer(&self, kb: &TemporalKnowledgeBase, query: &TemporalQuery)
560 -> Result<Vec<TemporalFact>>;
561
562 fn apply_rules(
564 &self,
565 facts: &[TemporalFact],
566 rules: &[TemporalRule],
567 ) -> Result<Vec<TemporalFact>>;
568}
569
570pub trait EventDetector: Send + Sync {
572 fn detect_events(
574 &self,
575 facts: &[TemporalFact],
576 event_definitions: &[EventDefinition],
577 ) -> Result<Vec<DetectedEvent>>;
578
579 fn get_patterns(&self) -> Vec<EventDetectionPattern>;
581}
582
583pub trait TemporalConstraintSolver: Send + Sync {
585 fn solve_constraints(&self, constraints: &[TemporalConstraint]) -> Result<ConstraintSolution>;
587
588 fn check_satisfaction(
590 &self,
591 constraints: &[TemporalConstraint],
592 assignments: &HashMap<String, Timestamp>,
593 ) -> Result<bool>;
594}
595
596#[derive(Debug, Clone, Serialize, Deserialize)]
598pub struct DetectedEvent {
599 pub event_type: String,
601
602 pub interval: TimeInterval,
604
605 pub participants: HashMap<String, String>,
607
608 pub supporting_facts: Vec<TemporalFact>,
610
611 pub confidence: f32,
613}
614
615#[derive(Debug, Clone, Serialize, Deserialize)]
617pub struct ConstraintSolution {
618 pub assignments: HashMap<String, Timestamp>,
620
621 pub satisfaction_score: f32,
623
624 pub unsatisfied: Vec<String>,
626}
627
628impl TemporalReasoner {
629 pub fn new(_config: &AiConfig) -> Result<Self> {
631 let temporal_config = TemporalConfig::default();
632
633 Ok(Self {
634 config: temporal_config,
635 temporal_kb: TemporalKnowledgeBase::new(),
636 inference_engine: Box::new(DefaultTemporalInferenceEngine::new()),
637 event_detector: Box::new(DefaultEventDetector::new()),
638 constraint_solver: Box::new(DefaultConstraintSolver::new()),
639 })
640 }
641
642 pub async fn reason(&self, query: &TemporalQuery) -> Result<TemporalResult> {
644 let start_time = std::time::Instant::now();
645 let mut inference_steps = Vec::new();
646
647 let mut facts = self.retrieve_facts(query)?;
649
650 if self.config.enable_inference && query.include_inferred {
652 let inferred_facts = self.inference_engine.infer(&self.temporal_kb, query)?;
653
654 for (idx, inferred_fact) in inferred_facts.iter().enumerate() {
656 if let FactSource::Inferred { rule, premises } = &inferred_fact.source {
657 let step = InferenceStep {
658 step: idx + 1,
659 rule: rule.clone(),
660 inputs: premises
661 .iter()
662 .filter_map(|premise_id| {
663 facts
664 .iter()
665 .find(|f| {
666 format!("{}:{}:{}", f.subject, f.predicate, f.object)
667 == *premise_id
668 })
669 .cloned()
670 })
671 .collect(),
672 output: inferred_fact.clone(),
673 confidence: inferred_fact.confidence,
674 };
675 inference_steps.push(step);
676 }
677 }
678
679 facts.extend(inferred_facts);
680 }
681
682 if self.config.enable_event_detection {
684 let events = self.event_detector.detect_events(
685 &facts,
686 &self
687 .temporal_kb
688 .event_definitions
689 .values()
690 .cloned()
691 .collect::<Vec<_>>(),
692 )?;
693
694 for event in events {
696 let event_fact = self.event_to_fact(event)?;
697 facts.push(event_fact);
698 }
699 }
700
701 let filtered_facts = self.filter_and_rank_facts(facts, query)?;
703
704 let overall_confidence = if filtered_facts.is_empty() {
706 0.0
707 } else {
708 let sum: f32 = filtered_facts.iter().map(|f| f.confidence).sum();
709 let count = filtered_facts.len() as f32;
710 (sum / count).min(1.0) };
712
713 let execution_time = start_time.elapsed();
714
715 Ok(TemporalResult {
716 query_id: format!("query_{}", {
717 let mut rng = Random::default();
718 rng.random::<u32>()
719 }),
720 results: filtered_facts,
721 inference_trace: if inference_steps.is_empty() {
722 None
723 } else {
724 Some(inference_steps)
725 },
726 execution_time,
727 confidence: overall_confidence,
728 })
729 }
730
731 pub fn add_fact(&mut self, fact: TemporalFact) -> Result<()> {
733 self.temporal_kb
735 .facts_by_time
736 .entry(fact.validity.start)
737 .or_default()
738 .push(fact.clone());
739
740 self.temporal_kb
742 .facts_by_entity
743 .entry(fact.subject.clone())
744 .or_default()
745 .push(fact.clone());
746
747 self.temporal_kb
748 .facts_by_entity
749 .entry(fact.object.clone())
750 .or_default()
751 .push(fact);
752
753 Ok(())
754 }
755
756 fn retrieve_facts(&self, query: &TemporalQuery) -> Result<Vec<TemporalFact>> {
758 let mut facts = Vec::new();
759
760 match &query.query_type {
762 TemporalQueryType::ValidAt { time } => {
763 for time_facts in self.temporal_kb.facts_by_time.values() {
764 for fact in time_facts {
765 if fact.validity.contains(*time) {
766 facts.push(fact.clone());
767 }
768 }
769 }
770 }
771 TemporalQueryType::ValidDuring { interval } => {
772 for time_facts in self.temporal_kb.facts_by_time.values() {
773 for fact in time_facts {
774 if fact.validity.overlaps(interval) {
775 facts.push(fact.clone());
776 }
777 }
778 }
779 }
780 _ => {
781 for time_facts in self.temporal_kb.facts_by_time.values() {
783 facts.extend(time_facts.clone());
784 }
785 }
786 }
787
788 Ok(facts)
789 }
790
791 fn event_to_fact(&self, event: DetectedEvent) -> Result<TemporalFact> {
793 Ok(TemporalFact {
794 subject: format!("event:{}", event.event_type),
795 predicate: "hasEventType".to_string(),
796 object: event.event_type,
797 validity: event.interval,
798 confidence: event.confidence,
799 source: FactSource::EventDetection {
800 detector: "default".to_string(),
801 },
802 annotations: HashMap::new(),
803 })
804 }
805
806 fn filter_and_rank_facts(
808 &self,
809 mut facts: Vec<TemporalFact>,
810 query: &TemporalQuery,
811 ) -> Result<Vec<TemporalFact>> {
812 if !query.entities.is_empty() {
814 facts.retain(|fact| {
815 query.entities.contains(&fact.subject) || query.entities.contains(&fact.object)
816 });
817 }
818
819 if let Some(window) = &query.time_window {
821 facts.retain(|fact| fact.validity.overlaps(window));
822 }
823
824 facts.sort_by(|a, b| {
826 b.confidence
827 .partial_cmp(&a.confidence)
828 .unwrap_or(std::cmp::Ordering::Equal)
829 });
830
831 Ok(facts)
832 }
833}
834
835impl TemporalKnowledgeBase {
836 fn new() -> Self {
837 Self {
838 facts_by_time: BTreeMap::new(),
839 facts_by_entity: HashMap::new(),
840 temporal_rules: Vec::new(),
841 event_definitions: HashMap::new(),
842 }
843 }
844}
845
846struct DefaultTemporalInferenceEngine;
848
849impl DefaultTemporalInferenceEngine {
850 fn new() -> Self {
851 Self
852 }
853}
854
855impl TemporalInferenceEngine for DefaultTemporalInferenceEngine {
856 fn infer(
857 &self,
858 _kb: &TemporalKnowledgeBase,
859 _query: &TemporalQuery,
860 ) -> Result<Vec<TemporalFact>> {
861 Ok(Vec::new())
863 }
864
865 fn apply_rules(
866 &self,
867 _facts: &[TemporalFact],
868 _rules: &[TemporalRule],
869 ) -> Result<Vec<TemporalFact>> {
870 Ok(Vec::new())
872 }
873}
874
875struct DefaultEventDetector;
877
878impl DefaultEventDetector {
879 fn new() -> Self {
880 Self
881 }
882}
883
884impl EventDetector for DefaultEventDetector {
885 fn detect_events(
886 &self,
887 _facts: &[TemporalFact],
888 _event_definitions: &[EventDefinition],
889 ) -> Result<Vec<DetectedEvent>> {
890 Ok(Vec::new())
892 }
893
894 fn get_patterns(&self) -> Vec<EventDetectionPattern> {
895 Vec::new()
896 }
897}
898
899struct DefaultConstraintSolver;
901
902impl DefaultConstraintSolver {
903 fn new() -> Self {
904 Self
905 }
906}
907
908impl TemporalConstraintSolver for DefaultConstraintSolver {
909 fn solve_constraints(&self, _constraints: &[TemporalConstraint]) -> Result<ConstraintSolution> {
910 Ok(ConstraintSolution {
912 assignments: HashMap::new(),
913 satisfaction_score: 1.0,
914 unsatisfied: Vec::new(),
915 })
916 }
917
918 fn check_satisfaction(
919 &self,
920 _constraints: &[TemporalConstraint],
921 _assignments: &HashMap<String, Timestamp>,
922 ) -> Result<bool> {
923 Ok(true)
925 }
926}
927
928pub fn current_timestamp() -> Timestamp {
930 SystemTime::now()
931 .duration_since(UNIX_EPOCH)
932 .expect("SystemTime should be after UNIX_EPOCH")
933 .as_secs()
934}
935
936#[cfg(test)]
937mod tests {
938 use super::*;
939 use crate::ai::AiConfig;
940
941 #[test]
942 fn test_temporal_reasoner_creation() {
943 let config = AiConfig::default();
944 let reasoner = TemporalReasoner::new(&config);
945 assert!(reasoner.is_ok());
946 }
947
948 #[test]
949 fn test_time_interval_operations() {
950 let interval1 = TimeInterval {
951 start: 100,
952 end: 200,
953 interval_type: IntervalType::Closed,
954 };
955
956 let interval2 = TimeInterval {
957 start: 150,
958 end: 250,
959 interval_type: IntervalType::Closed,
960 };
961
962 assert!(interval1.overlaps(&interval2));
963 assert_eq!(
964 interval1.relation_to(&interval2),
965 TemporalRelation::Overlaps
966 );
967 }
968
969 #[test]
970 fn test_temporal_fact_creation() {
971 let fact = TemporalFact {
972 subject: "http://example.org/person1".to_string(),
973 predicate: "worksFor".to_string(),
974 object: "http://example.org/company1".to_string(),
975 validity: TimeInterval {
976 start: 1000,
977 end: 2000,
978 interval_type: IntervalType::Closed,
979 },
980 confidence: 0.9,
981 source: FactSource::Asserted,
982 annotations: HashMap::new(),
983 };
984
985 assert_eq!(fact.confidence, 0.9);
986 assert!(fact.validity.contains(1500));
987 assert!(!fact.validity.contains(2500));
988 }
989
990 #[tokio::test]
991 async fn test_temporal_query() {
992 let config = AiConfig::default();
993 let reasoner = TemporalReasoner::new(&config).expect("construction should succeed");
994
995 let query = TemporalQuery {
996 query_type: TemporalQueryType::ValidAt {
997 time: current_timestamp(),
998 },
999 entities: vec!["http://example.org/person1".to_string()],
1000 constraints: Vec::new(),
1001 time_window: None,
1002 include_inferred: false,
1003 };
1004
1005 let result = reasoner
1006 .reason(&query)
1007 .await
1008 .expect("async operation should succeed");
1009 assert!(!result.query_id.is_empty());
1010 }
1011}