forge_reasoning/belief/
mod.rs1mod graph;
8
9pub use graph::BeliefGraph;
10
11use crate::hypothesis::HypothesisBoard;
12use crate::hypothesis::types::HypothesisId;
13use crate::errors::Result;
14
15pub struct ReasoningSystem {
17 pub board: HypothesisBoard,
18 pub graph: BeliefGraph,
19}
20
21impl ReasoningSystem {
22 pub fn new(board: HypothesisBoard) -> Self {
23 Self {
24 board,
25 graph: BeliefGraph::new(),
26 }
27 }
28
29 pub fn in_memory() -> Self {
30 Self::new(HypothesisBoard::in_memory())
31 }
32
33 pub async fn add_dependency(
35 &mut self,
36 hypothesis_id: HypothesisId,
37 depends_on: HypothesisId,
38 ) -> Result<()> {
39 if self.board.get(hypothesis_id).await?.is_none() {
41 return Err(crate::errors::ReasoningError::NotFound(
42 format!("Hypothesis {} not found", hypothesis_id)
43 ));
44 }
45 if self.board.get(depends_on).await?.is_none() {
46 return Err(crate::errors::ReasoningError::NotFound(
47 format!("Hypothesis {} not found", depends_on)
48 ));
49 }
50
51 self.graph.add_dependency(hypothesis_id, depends_on)
52 }
53
54 pub fn remove_dependency(
56 &mut self,
57 hypothesis_id: HypothesisId,
58 depends_on: HypothesisId,
59 ) -> Result<bool> {
60 self.graph.remove_dependency(hypothesis_id, depends_on)
61 }
62
63 pub fn dependents(&self, hypothesis_id: HypothesisId) -> Result<indexmap::IndexSet<HypothesisId>> {
65 self.graph.dependents(hypothesis_id)
66 }
67
68 pub fn dependees(&self, hypothesis_id: HypothesisId) -> Result<indexmap::IndexSet<HypothesisId>> {
70 self.graph.dependees(hypothesis_id)
71 }
72
73 pub fn dependency_chain(&self, hypothesis_id: HypothesisId) -> Result<indexmap::IndexSet<HypothesisId>> {
75 self.graph.dependency_chain(hypothesis_id)
76 }
77
78 pub fn detect_cycles(&self) -> Vec<Vec<HypothesisId>> {
80 self.graph.detect_cycles()
81 }
82
83 pub async fn remove_hypothesis(&mut self, id: HypothesisId) -> Result<bool> {
85 let removed_from_board = self.board.delete(id).await?;
86 let removed_from_graph = self.graph.remove_hypothesis(id)?;
87 Ok(removed_from_board || removed_from_graph)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use crate::hypothesis::confidence::Confidence;
95
96 #[tokio::test]
97 async fn test_reasoning_system_integration() {
98 let mut system = ReasoningSystem::in_memory();
99
100 let h1 = system.board.propose("H1", Confidence::new(0.5).unwrap()).await.unwrap();
102 let h2 = system.board.propose("H2", Confidence::new(0.5).unwrap()).await.unwrap();
103 let h3 = system.board.propose("H3", Confidence::new(0.5).unwrap()).await.unwrap();
104
105 system.add_dependency(h1, h2).await.unwrap();
107 system.add_dependency(h2, h3).await.unwrap();
108
109 let chain = system.dependency_chain(h1).unwrap();
111 assert_eq!(chain.len(), 2);
112 assert!(chain.contains(&h2));
113 assert!(chain.contains(&h3));
114
115 assert_eq!(system.detect_cycles().len(), 0);
117 }
118
119 #[tokio::test]
120 async fn test_cycle_prevention() {
121 let mut system = ReasoningSystem::in_memory();
122
123 let h1 = system.board.propose("H1", Confidence::new(0.5).unwrap()).await.unwrap();
124 let h2 = system.board.propose("H2", Confidence::new(0.5).unwrap()).await.unwrap();
125
126 system.add_dependency(h1, h2).await.unwrap();
127
128 let result = system.add_dependency(h2, h1).await;
130 assert!(result.is_err());
131 }
132}