1use crate::rag::AssembledContext;
11use anyhow::{anyhow, Result};
12use chrono::{DateTime, Utc};
13use oxirs_core::model::triple::Triple;
14use regex::Regex;
15use serde::{Deserialize, Serialize};
16use std::collections::HashMap;
17use tracing::{debug, info};
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct ReasoningConfig {
22 pub max_inference_depth: usize,
23 pub confidence_threshold: f64,
24 pub enable_causal_reasoning: bool,
25 pub enable_temporal_reasoning: bool,
26 pub enable_analogical_reasoning: bool,
27 pub uncertainty_quantification: bool,
28}
29
30impl Default for ReasoningConfig {
31 fn default() -> Self {
32 Self {
33 max_inference_depth: 5,
34 confidence_threshold: 0.7,
35 enable_causal_reasoning: true,
36 enable_temporal_reasoning: true,
37 enable_analogical_reasoning: true,
38 uncertainty_quantification: true,
39 }
40 }
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
45pub enum ReasoningType {
46 Deductive,
48 Inductive,
50 Causal,
52 Temporal,
54 Analogical,
56 Probabilistic,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct ReasoningStep {
63 pub step_id: String,
64 pub reasoning_type: ReasoningType,
65 pub premise_triples: Vec<Triple>,
66 pub conclusion_triple: Option<Triple>,
67 pub confidence: f64,
68 pub explanation: String,
69 pub timestamp: DateTime<Utc>,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct ReasoningChain {
75 pub chain_id: String,
76 pub query: String,
77 pub steps: Vec<ReasoningStep>,
78 pub final_conclusion: Option<Triple>,
79 pub overall_confidence: f64,
80 pub reasoning_time_ms: u64,
81 pub alternative_chains: Vec<AlternativeChain>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct AlternativeChain {
87 pub chain_id: String,
88 pub steps: Vec<ReasoningStep>,
89 pub conclusion: Option<Triple>,
90 pub confidence: f64,
91 pub divergence_point: usize,
92}
93
94#[derive(Debug, Clone)]
96pub struct ReasoningResult {
97 pub primary_chain: ReasoningChain,
98 pub supporting_evidence: Vec<Triple>,
99 pub contradicting_evidence: Vec<Triple>,
100 pub uncertainty_factors: Vec<UncertaintyFactor>,
101 pub reasoning_quality: ReasoningQuality,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct UncertaintyFactor {
107 pub factor_type: UncertaintyType,
108 pub description: String,
109 pub impact_score: f64,
110 pub mitigation_strategy: Option<String>,
111}
112
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
115pub enum UncertaintyType {
116 InsufficientEvidence,
118 ConflictingEvidence,
120 TemporalInconsistency,
122 CausalGaps,
124 StatisticalUncertainty,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct ReasoningQuality {
131 pub logical_consistency: f64,
132 pub evidence_strength: f64,
133 pub chain_completeness: f64,
134 pub temporal_coherence: f64,
135 pub overall_quality: f64,
136}
137
138pub struct AdvancedReasoningEngine {
140 config: ReasoningConfig,
141 reasoning_patterns: HashMap<String, ReasoningPattern>,
142 causal_knowledge: CausalKnowledgeBase,
143 temporal_model: TemporalReasoningModel,
144 analogical_matcher: AnalogicalMatcher,
145}
146
147#[derive(Debug, Clone)]
149struct ReasoningPattern {
150 pattern_id: String,
151 pattern_type: ReasoningType,
152 premise_template: String,
153 conclusion_template: String,
154 confidence_modifier: f64,
155}
156
157#[derive(Debug, Clone)]
159struct CausalKnowledgeBase {
160 causal_relations: HashMap<String, Vec<CausalRelation>>,
161}
162
163#[derive(Debug, Clone)]
165struct TemporalReasoningModel {
166 temporal_relations: HashMap<String, TemporalRelation>,
167 time_constraints: Vec<TimeConstraint>,
168}
169
170#[derive(Debug, Clone)]
172struct AnalogicalMatcher {
173 similarity_patterns: HashMap<String, Vec<AnalogicalPattern>>,
174}
175
176#[derive(Debug, Clone)]
177struct CausalRelation {
178 cause: String,
179 effect: String,
180 strength: f64,
181 conditions: Vec<String>,
182}
183
184#[derive(Debug, Clone)]
185struct TemporalRelation {
186 relation_type: String,
187 before_entity: String,
188 after_entity: String,
189 time_interval: Option<std::time::Duration>,
190}
191
192#[derive(Debug, Clone)]
193struct TimeConstraint {
194 constraint_type: String,
195 entities: Vec<String>,
196 temporal_bound: DateTime<Utc>,
197}
198
199#[derive(Debug, Clone)]
200struct AnalogicalPattern {
201 source_domain: String,
202 target_domain: String,
203 mapping_strength: f64,
204 structural_similarity: f64,
205}
206
207impl AdvancedReasoningEngine {
208 pub fn new(config: ReasoningConfig) -> Self {
210 Self {
211 config,
212 reasoning_patterns: Self::initialize_reasoning_patterns(),
213 causal_knowledge: CausalKnowledgeBase {
214 causal_relations: HashMap::new(),
215 },
216 temporal_model: TemporalReasoningModel {
217 temporal_relations: HashMap::new(),
218 time_constraints: Vec::new(),
219 },
220 analogical_matcher: AnalogicalMatcher {
221 similarity_patterns: HashMap::new(),
222 },
223 }
224 }
225
226 fn initialize_reasoning_patterns() -> HashMap<String, ReasoningPattern> {
228 let mut patterns = HashMap::new();
229
230 patterns.insert(
232 "modus_ponens".to_string(),
233 ReasoningPattern {
234 pattern_id: "modus_ponens".to_string(),
235 pattern_type: ReasoningType::Deductive,
236 premise_template: "If {P} then {Q}; {P} is true".to_string(),
237 conclusion_template: "Therefore {Q} is true".to_string(),
238 confidence_modifier: 0.95,
239 },
240 );
241
242 patterns.insert(
244 "causal_chain".to_string(),
245 ReasoningPattern {
246 pattern_id: "causal_chain".to_string(),
247 pattern_type: ReasoningType::Causal,
248 premise_template: "{A} causes {B}; {B} causes {C}".to_string(),
249 conclusion_template: "{A} causes {C}".to_string(),
250 confidence_modifier: 0.8,
251 },
252 );
253
254 patterns.insert(
256 "temporal_sequence".to_string(),
257 ReasoningPattern {
258 pattern_id: "temporal_sequence".to_string(),
259 pattern_type: ReasoningType::Temporal,
260 premise_template: "{A} happens before {B}; {B} happens before {C}".to_string(),
261 conclusion_template: "{A} happens before {C}".to_string(),
262 confidence_modifier: 0.9,
263 },
264 );
265
266 patterns
267 }
268
269 pub async fn reason(
271 &mut self,
272 query: &str,
273 context: &AssembledContext,
274 ) -> Result<ReasoningResult> {
275 let start_time = std::time::Instant::now();
276 info!("Starting advanced reasoning for query: {}", query);
277
278 let mut reasoning_chains = Vec::new();
280
281 if let Some(deductive_chain) = self.build_deductive_chain(query, context).await? {
283 reasoning_chains.push(deductive_chain);
284 }
285
286 if self.config.enable_causal_reasoning {
288 if let Some(causal_chain) = self.build_causal_chain(query, context).await? {
289 reasoning_chains.push(causal_chain);
290 }
291 }
292
293 if self.config.enable_temporal_reasoning {
295 if let Some(temporal_chain) = self.build_temporal_chain(query, context).await? {
296 reasoning_chains.push(temporal_chain);
297 }
298 }
299
300 if self.config.enable_analogical_reasoning {
302 if let Some(analogical_chain) = self.build_analogical_chain(query, context).await? {
303 reasoning_chains.push(analogical_chain);
304 }
305 }
306
307 let primary_chain = self.select_best_chain(reasoning_chains)?;
309
310 let (supporting_evidence, contradicting_evidence) =
312 self.gather_evidence(&primary_chain, context).await?;
313
314 let uncertainty_factors = if self.config.uncertainty_quantification {
316 self.quantify_uncertainty(&primary_chain, context).await?
317 } else {
318 Vec::new()
319 };
320
321 let reasoning_quality = self
323 .assess_reasoning_quality(&primary_chain, context)
324 .await?;
325
326 let reasoning_time = start_time.elapsed().as_millis() as u64;
327 info!("Advanced reasoning completed in {}ms", reasoning_time);
328
329 Ok(ReasoningResult {
330 primary_chain,
331 supporting_evidence,
332 contradicting_evidence,
333 uncertainty_factors,
334 reasoning_quality,
335 })
336 }
337
338 async fn build_deductive_chain(
340 &self,
341 query: &str,
342 context: &AssembledContext,
343 ) -> Result<Option<ReasoningChain>> {
344 debug!("Building deductive reasoning chain");
345
346 let mut steps = Vec::new();
347 let mut current_premises = context
348 .semantic_results
349 .iter()
350 .map(|r| r.triple.clone())
351 .collect::<Vec<_>>();
352
353 for depth in 0..self.config.max_inference_depth {
355 if let Some(new_conclusion) = self.apply_modus_ponens(¤t_premises)? {
356 let step = ReasoningStep {
357 step_id: format!("deductive_step_{depth}"),
358 reasoning_type: ReasoningType::Deductive,
359 premise_triples: current_premises.clone(),
360 conclusion_triple: Some(new_conclusion.clone()),
361 confidence: 0.9 - (depth as f64 * 0.1),
362 explanation: format!("Applied deductive inference at depth {depth}"),
363 timestamp: Utc::now(),
364 };
365 steps.push(step);
366 current_premises.push(new_conclusion);
367 } else {
368 break;
369 }
370 }
371
372 if steps.is_empty() {
373 return Ok(None);
374 }
375
376 let overall_confidence = steps
377 .iter()
378 .map(|s| s.confidence)
379 .fold(1.0, |acc, conf| acc * conf);
380
381 Ok(Some(ReasoningChain {
382 chain_id: uuid::Uuid::new_v4().to_string(),
383 query: query.to_string(),
384 steps,
385 final_conclusion: current_premises.last().cloned(),
386 overall_confidence,
387 reasoning_time_ms: 0, alternative_chains: Vec::new(),
389 }))
390 }
391
392 async fn build_causal_chain(
394 &self,
395 query: &str,
396 context: &AssembledContext,
397 ) -> Result<Option<ReasoningChain>> {
398 debug!("Building causal reasoning chain");
399
400 let causal_triples = context
402 .semantic_results
403 .iter()
404 .filter(|r| self.is_causal_relation(&r.triple))
405 .map(|r| r.triple.clone())
406 .collect::<Vec<_>>();
407
408 if causal_triples.is_empty() {
409 return Ok(None);
410 }
411
412 let mut steps = Vec::new();
413 let mut causal_chain = Vec::new();
414
415 for (i, triple) in causal_triples.iter().enumerate() {
417 let step = ReasoningStep {
418 step_id: format!("causal_step_{i}"),
419 reasoning_type: ReasoningType::Causal,
420 premise_triples: vec![triple.clone()],
421 conclusion_triple: None, confidence: 0.8,
423 explanation: format!("Identified causal relationship: {}", triple.object()),
424 timestamp: Utc::now(),
425 };
426 steps.push(step);
427 causal_chain.push(triple.clone());
428 }
429
430 let overall_confidence = 0.8_f64.powi(steps.len() as i32);
431
432 Ok(Some(ReasoningChain {
433 chain_id: uuid::Uuid::new_v4().to_string(),
434 query: query.to_string(),
435 steps,
436 final_conclusion: causal_chain.last().cloned(),
437 overall_confidence,
438 reasoning_time_ms: 0,
439 alternative_chains: Vec::new(),
440 }))
441 }
442
443 async fn build_temporal_chain(
445 &self,
446 query: &str,
447 context: &AssembledContext,
448 ) -> Result<Option<ReasoningChain>> {
449 debug!("Building temporal reasoning chain");
450
451 let temporal_triples = context
453 .semantic_results
454 .iter()
455 .filter(|r| self.is_temporal_relation(&r.triple))
456 .map(|r| r.triple.clone())
457 .collect::<Vec<_>>();
458
459 if temporal_triples.is_empty() {
460 return Ok(None);
461 }
462
463 let mut sorted_triples = temporal_triples;
465
466 sorted_triples.sort_by(|a, b| {
468 let a_temporal_score = self.extract_temporal_score(a);
470 let b_temporal_score = self.extract_temporal_score(b);
471
472 a_temporal_score
473 .partial_cmp(&b_temporal_score)
474 .unwrap_or(std::cmp::Ordering::Equal)
475 });
476
477 let mut steps = Vec::new();
478 for (i, triple) in sorted_triples.iter().enumerate() {
479 let step = ReasoningStep {
480 step_id: format!("temporal_step_{i}"),
481 reasoning_type: ReasoningType::Temporal,
482 premise_triples: vec![triple.clone()],
483 conclusion_triple: None,
484 confidence: 0.85,
485 explanation: format!("Temporal sequence element: {}", triple.object()),
486 timestamp: Utc::now(),
487 };
488 steps.push(step);
489 }
490
491 let overall_confidence = 0.85_f64.powi(steps.len() as i32);
492
493 Ok(Some(ReasoningChain {
494 chain_id: uuid::Uuid::new_v4().to_string(),
495 query: query.to_string(),
496 steps,
497 final_conclusion: sorted_triples.last().cloned(),
498 overall_confidence,
499 reasoning_time_ms: 0,
500 alternative_chains: Vec::new(),
501 }))
502 }
503
504 async fn build_analogical_chain(
506 &self,
507 query: &str,
508 context: &AssembledContext,
509 ) -> Result<Option<ReasoningChain>> {
510 debug!("Building analogical reasoning chain");
511
512 let analogical_candidates = context
514 .semantic_results
515 .iter()
516 .filter(|r| self.has_analogical_potential(&r.triple))
517 .map(|r| r.triple.clone())
518 .collect::<Vec<_>>();
519
520 if analogical_candidates.is_empty() {
521 return Ok(None);
522 }
523
524 let mut steps = Vec::new();
525 for (i, triple) in analogical_candidates.iter().enumerate() {
526 let step = ReasoningStep {
527 step_id: format!("analogical_step_{i}"),
528 reasoning_type: ReasoningType::Analogical,
529 premise_triples: vec![triple.clone()],
530 conclusion_triple: None,
531 confidence: 0.7, explanation: format!("Analogical pattern identified: {}", triple.object()),
533 timestamp: Utc::now(),
534 };
535 steps.push(step);
536 }
537
538 let overall_confidence = 0.7_f64.powi(steps.len() as i32);
539
540 Ok(Some(ReasoningChain {
541 chain_id: uuid::Uuid::new_v4().to_string(),
542 query: query.to_string(),
543 steps,
544 final_conclusion: analogical_candidates.last().cloned(),
545 overall_confidence,
546 reasoning_time_ms: 0,
547 alternative_chains: Vec::new(),
548 }))
549 }
550
551 fn apply_modus_ponens(&self, premises: &[Triple]) -> Result<Option<Triple>> {
553 for premise in premises {
557 let predicate_str = premise.predicate().to_string();
559 if predicate_str.contains("implies") || predicate_str.contains("causes") {
560 return Ok(Some(premise.clone()));
563 }
564 }
565
566 Ok(None)
567 }
568
569 fn is_causal_relation(&self, triple: &Triple) -> bool {
571 let predicate = triple.predicate().to_string().to_lowercase();
572 predicate.contains("cause")
573 || predicate.contains("result")
574 || predicate.contains("lead")
575 || predicate.contains("effect")
576 }
577
578 fn is_temporal_relation(&self, triple: &Triple) -> bool {
580 let predicate = triple.predicate().to_string().to_lowercase();
581 predicate.contains("before")
582 || predicate.contains("after")
583 || predicate.contains("during")
584 || predicate.contains("when")
585 || predicate.contains("time")
586 }
587
588 fn has_analogical_potential(&self, triple: &Triple) -> bool {
590 let predicate = triple.predicate().to_string().to_lowercase();
591 predicate.contains("similar")
592 || predicate.contains("like")
593 || predicate.contains("analogy")
594 || predicate.contains("resemble")
595 }
596
597 fn select_best_chain(&self, chains: Vec<ReasoningChain>) -> Result<ReasoningChain> {
599 if chains.is_empty() {
600 return Err(anyhow!("No valid reasoning chains found"));
601 }
602
603 let best_chain = chains
605 .into_iter()
606 .filter(|chain| chain.overall_confidence >= self.config.confidence_threshold)
607 .max_by(|a, b| {
608 a.overall_confidence
609 .partial_cmp(&b.overall_confidence)
610 .unwrap()
611 });
612
613 best_chain.ok_or_else(|| anyhow!("No reasoning chain meets confidence threshold"))
614 }
615
616 async fn gather_evidence(
618 &self,
619 _chain: &ReasoningChain,
620 context: &AssembledContext,
621 ) -> Result<(Vec<Triple>, Vec<Triple>)> {
622 let mut supporting = Vec::new();
623 let mut contradicting = Vec::new();
624
625 for result in &context.semantic_results {
627 if result.score > 0.8 {
628 supporting.push(result.triple.clone());
629 } else if result.score < 0.3 {
630 contradicting.push(result.triple.clone());
631 }
632 }
633
634 Ok((supporting, contradicting))
635 }
636
637 async fn quantify_uncertainty(
639 &self,
640 chain: &ReasoningChain,
641 context: &AssembledContext,
642 ) -> Result<Vec<UncertaintyFactor>> {
643 let mut factors = Vec::new();
644
645 if context.semantic_results.len() < 3 {
647 factors.push(UncertaintyFactor {
648 factor_type: UncertaintyType::InsufficientEvidence,
649 description: "Limited evidence available for reasoning".to_string(),
650 impact_score: 0.3,
651 mitigation_strategy: Some("Gather more relevant information".to_string()),
652 });
653 }
654
655 let confidence_variance = chain
657 .steps
658 .iter()
659 .map(|s| s.confidence)
660 .fold((0.0, 0.0), |acc, conf| (acc.0 + conf, acc.1 + conf * conf));
661
662 let mean_confidence = confidence_variance.0 / chain.steps.len() as f64;
663 let variance =
664 (confidence_variance.1 / chain.steps.len() as f64) - mean_confidence * mean_confidence;
665
666 if variance > 0.1 {
667 factors.push(UncertaintyFactor {
668 factor_type: UncertaintyType::ConflictingEvidence,
669 description: "High variance in step confidences".to_string(),
670 impact_score: variance,
671 mitigation_strategy: Some("Resolve conflicting information".to_string()),
672 });
673 }
674
675 Ok(factors)
676 }
677
678 async fn assess_reasoning_quality(
680 &self,
681 chain: &ReasoningChain,
682 context: &AssembledContext,
683 ) -> Result<ReasoningQuality> {
684 let logical_consistency = chain
686 .steps
687 .iter()
688 .map(|s| s.confidence)
689 .fold(0.0, |acc, conf| acc + conf)
690 / chain.steps.len() as f64;
691
692 let evidence_strength = context
694 .semantic_results
695 .iter()
696 .map(|r| r.score as f64)
697 .fold(0.0, |acc, score| acc + score)
698 / context.semantic_results.len().max(1) as f64;
699
700 let chain_completeness = if chain.final_conclusion.is_some() {
702 1.0
703 } else {
704 0.5
705 };
706
707 let temporal_coherence = self.analyze_temporal_coherence(chain);
709
710 let overall_quality =
711 (logical_consistency + evidence_strength + chain_completeness + temporal_coherence)
712 / 4.0;
713
714 Ok(ReasoningQuality {
715 logical_consistency,
716 evidence_strength,
717 chain_completeness,
718 temporal_coherence,
719 overall_quality,
720 })
721 }
722
723 fn extract_temporal_score(&self, triple: &Triple) -> f64 {
725 let object_str = triple.object().to_string().to_lowercase();
726
727 if object_str.contains("before")
729 || object_str.contains("first")
730 || object_str.contains("initial")
731 {
732 0.0
733 } else if object_str.contains("during")
734 || object_str.contains("while")
735 || object_str.contains("concurrent")
736 {
737 0.5
738 } else if object_str.contains("after")
739 || object_str.contains("then")
740 || object_str.contains("following")
741 {
742 1.0
743 } else if object_str.contains("finally")
744 || object_str.contains("last")
745 || object_str.contains("end")
746 {
747 2.0
748 } else {
749 if let Some(year) = self.extract_year_from_string(&object_str) {
751 year as f64 / 10000.0 } else {
753 0.5 }
755 }
756 }
757
758 fn extract_year_from_string(&self, text: &str) -> Option<i32> {
760 let year_regex = Regex::new(r"\b(1[0-9]{3}|20[0-9]{2}|21[0-9]{2})\b").ok()?;
762 if let Some(captures) = year_regex.find(text) {
763 captures.as_str().parse().ok()
764 } else {
765 None
766 }
767 }
768
769 fn analyze_temporal_coherence(&self, chain: &ReasoningChain) -> f64 {
771 if chain.steps.len() < 2 {
772 return 1.0; }
774
775 let mut coherence_scores = Vec::new();
776
777 for i in 1..chain.steps.len() {
778 let prev_step = &chain.steps[i - 1];
779 let curr_step = &chain.steps[i];
780
781 let prev_temporal = self.extract_temporal_info_from_step(prev_step);
783 let curr_temporal = self.extract_temporal_info_from_step(curr_step);
784
785 let coherence = if prev_temporal <= curr_temporal {
786 1.0 } else {
788 0.3 };
790
791 coherence_scores.push(coherence);
792 }
793
794 coherence_scores.iter().sum::<f64>() / coherence_scores.len() as f64
795 }
796
797 fn extract_temporal_info_from_step(&self, step: &ReasoningStep) -> f64 {
799 if let Some(conclusion) = &step.conclusion_triple {
800 self.extract_temporal_score(conclusion)
801 } else if !step.premise_triples.is_empty() {
802 self.extract_temporal_score(&step.premise_triples[0])
803 } else {
804 0.5 }
806 }
807}
808
809#[cfg(test)]
810mod tests {
811 use super::*;
812
813 #[tokio::test]
814 async fn test_reasoning_engine_creation() {
815 let config = ReasoningConfig::default();
816 let engine = AdvancedReasoningEngine::new(config);
817
818 assert_eq!(engine.config.max_inference_depth, 5);
819 assert_eq!(engine.config.confidence_threshold, 0.7);
820 }
821
822 #[test]
823 fn test_reasoning_patterns_initialization() {
824 let patterns = AdvancedReasoningEngine::initialize_reasoning_patterns();
825
826 assert!(patterns.contains_key("modus_ponens"));
827 assert!(patterns.contains_key("causal_chain"));
828 assert!(patterns.contains_key("temporal_sequence"));
829 }
830
831 #[test]
832 fn test_causal_relation_detection() {
833 let _engine = AdvancedReasoningEngine::new(ReasoningConfig::default());
834
835 }
838}