Skip to main content

ronn_memory/
semantic.rs

1//! Semantic Memory - Long-term knowledge graph
2
3use crate::{MemoryId, Result};
4use std::collections::{HashMap, HashSet};
5
6/// A concept in semantic memory
7#[derive(Clone, Debug)]
8pub struct Concept {
9    pub id: MemoryId,
10    pub name: String,
11    pub activation: f64,
12    pub related_concepts: HashSet<MemoryId>,
13}
14
15/// Knowledge graph of concepts and relationships
16pub struct ConceptGraph {
17    concepts: HashMap<MemoryId, Concept>,
18    relationships: HashMap<(MemoryId, MemoryId), f64>, // (from, to) -> strength
19}
20
21impl ConceptGraph {
22    /// Create a new concept graph
23    pub fn new() -> Self {
24        Self {
25            concepts: HashMap::new(),
26            relationships: HashMap::new(),
27        }
28    }
29
30    /// Add a concept
31    pub fn add_concept(&mut self, concept: Concept) {
32        self.concepts.insert(concept.id, concept);
33    }
34
35    /// Add a relationship between concepts
36    pub fn add_relationship(&mut self, from_id: MemoryId, to_id: MemoryId, strength: f64) {
37        self.relationships.insert((from_id, to_id), strength);
38
39        // Update related concepts
40        if let Some(concept) = self.concepts.get_mut(&from_id) {
41            concept.related_concepts.insert(to_id);
42        }
43        if let Some(concept) = self.concepts.get_mut(&to_id) {
44            concept.related_concepts.insert(from_id);
45        }
46    }
47
48    /// Get related concepts
49    pub fn get_related(&self, id: MemoryId) -> Vec<&Concept> {
50        self.concepts
51            .get(&id)
52            .map(|concept| {
53                concept
54                    .related_concepts
55                    .iter()
56                    .filter_map(|rel_id| self.concepts.get(rel_id))
57                    .collect()
58            })
59            .unwrap_or_default()
60    }
61
62    /// Activate a concept (spreads activation to related concepts)
63    pub fn activate(&mut self, id: MemoryId, amount: f64) {
64        if let Some(concept) = self.concepts.get_mut(&id) {
65            concept.activation += amount;
66
67            // Spread activation to related concepts
68            let related: Vec<MemoryId> = concept.related_concepts.iter().copied().collect();
69            for rel_id in related {
70                if let Some(rel_concept) = self.concepts.get_mut(&rel_id) {
71                    // Spread with decay
72                    rel_concept.activation += amount * 0.5;
73                }
74            }
75        }
76    }
77
78    /// Get number of concepts
79    pub fn len(&self) -> usize {
80        self.concepts.len()
81    }
82
83    /// Check if empty
84    pub fn is_empty(&self) -> bool {
85        self.concepts.is_empty()
86    }
87}
88
89impl Default for ConceptGraph {
90    fn default() -> Self {
91        Self::new()
92    }
93}
94
95/// Semantic memory with concept storage and activation spreading
96pub struct SemanticMemory {
97    graph: ConceptGraph,
98}
99
100impl SemanticMemory {
101    /// Create new semantic memory
102    pub fn new() -> Self {
103        Self {
104            graph: ConceptGraph::new(),
105        }
106    }
107
108    /// Store a concept
109    pub fn store_concept(&mut self, concept: Concept) -> Result<()> {
110        self.graph.add_concept(concept);
111        Ok(())
112    }
113
114    /// Get number of concepts
115    pub fn len(&self) -> usize {
116        self.graph.len()
117    }
118
119    /// Check if empty
120    pub fn is_empty(&self) -> bool {
121        self.graph.is_empty()
122    }
123
124    /// Get the knowledge graph
125    pub fn graph(&self) -> &ConceptGraph {
126        &self.graph
127    }
128
129    /// Get mutable knowledge graph
130    pub fn graph_mut(&mut self) -> &mut ConceptGraph {
131        &mut self.graph
132    }
133}
134
135impl Default for SemanticMemory {
136    fn default() -> Self {
137        Self::new()
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
145    use super::*;
146
147    #[test]
148    fn test_concept_storage() {
149        let mut sm = SemanticMemory::new();
150
151        let concept = Concept {
152            id: 1,
153            name: "test".to_string(),
154            activation: 0.5,
155            related_concepts: HashSet::new(),
156        };
157
158        sm.store_concept(concept).unwrap();
159        assert_eq!(sm.len(), 1);
160    }
161
162    #[test]
163    fn test_activation_spreading() {
164        let mut graph = ConceptGraph::new();
165
166        // Create connected concepts
167        let c1 = Concept {
168            id: 1,
169            name: "A".to_string(),
170            activation: 0.0,
171            related_concepts: HashSet::new(),
172        };
173        let c2 = Concept {
174            id: 2,
175            name: "B".to_string(),
176            activation: 0.0,
177            related_concepts: HashSet::new(),
178        };
179
180        graph.add_concept(c1);
181        graph.add_concept(c2);
182        graph.add_relationship(1, 2, 0.8);
183
184        // Activate first concept
185        graph.activate(1, 1.0);
186
187        // Check activation spread
188        assert!(graph.concepts.get(&1).unwrap().activation >= 1.0);
189        assert!(graph.concepts.get(&2).unwrap().activation > 0.0);
190    }
191}