tensorlogic_adapters/
signature_matcher.rs1use std::collections::HashMap;
8
9use crate::PredicateInfo;
10
11#[derive(Clone, Debug, Default)]
42pub struct SignatureMatcher {
43 by_arity: HashMap<usize, Vec<String>>,
45 by_signature: HashMap<Vec<String>, Vec<String>>,
47 by_domain_set: HashMap<Vec<String>, Vec<String>>,
49 predicates: HashMap<String, PredicateInfo>,
51}
52
53impl SignatureMatcher {
54 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub fn add_predicate(&mut self, pred: &PredicateInfo) {
61 let name = pred.name.clone();
62 let arity = pred.arg_domains.len();
63 let signature = pred.arg_domains.clone();
64
65 self.by_arity.entry(arity).or_default().push(name.clone());
67
68 self.by_signature
70 .entry(signature.clone())
71 .or_default()
72 .push(name.clone());
73
74 let mut sorted_sig = signature.clone();
76 sorted_sig.sort();
77 self.by_domain_set
78 .entry(sorted_sig)
79 .or_default()
80 .push(name.clone());
81
82 self.predicates.insert(name, pred.clone());
84 }
85
86 pub fn remove_predicate(&mut self, name: &str) {
88 if let Some(pred) = self.predicates.remove(name) {
89 let arity = pred.arg_domains.len();
90 let signature = pred.arg_domains.clone();
91
92 if let Some(names) = self.by_arity.get_mut(&arity) {
94 names.retain(|n| n != name);
95 if names.is_empty() {
96 self.by_arity.remove(&arity);
97 }
98 }
99
100 if let Some(names) = self.by_signature.get_mut(&signature) {
102 names.retain(|n| n != name);
103 if names.is_empty() {
104 self.by_signature.remove(&signature);
105 }
106 }
107
108 let mut sorted_sig = signature;
110 sorted_sig.sort();
111 if let Some(names) = self.by_domain_set.get_mut(&sorted_sig) {
112 names.retain(|n| n != name);
113 if names.is_empty() {
114 self.by_domain_set.remove(&sorted_sig);
115 }
116 }
117 }
118 }
119
120 pub fn find_by_arity(&self, arity: usize) -> Vec<String> {
136 self.by_arity.get(&arity).cloned().unwrap_or_default()
137 }
138
139 pub fn find_by_signature(&self, signature: &[String]) -> Vec<String> {
154 self.by_signature
155 .get(signature)
156 .cloned()
157 .unwrap_or_default()
158 }
159
160 pub fn find_by_domain_set(&self, domains: &[String]) -> Vec<String> {
179 let mut sorted = domains.to_vec();
180 sorted.sort();
181 self.by_domain_set.get(&sorted).cloned().unwrap_or_default()
182 }
183
184 pub fn get_predicate(&self, name: &str) -> Option<&PredicateInfo> {
186 self.predicates.get(name)
187 }
188
189 pub fn contains(&self, name: &str) -> bool {
191 self.predicates.contains_key(name)
192 }
193
194 pub fn len(&self) -> usize {
196 self.predicates.len()
197 }
198
199 pub fn is_empty(&self) -> bool {
201 self.predicates.is_empty()
202 }
203
204 pub fn predicate_names(&self) -> Vec<String> {
206 self.predicates.keys().cloned().collect()
207 }
208
209 pub fn clear(&mut self) {
211 self.by_arity.clear();
212 self.by_signature.clear();
213 self.by_domain_set.clear();
214 self.predicates.clear();
215 }
216
217 pub fn stats(&self) -> MatcherStats {
219 MatcherStats {
220 total_predicates: self.predicates.len(),
221 unique_arities: self.by_arity.len(),
222 unique_signatures: self.by_signature.len(),
223 unique_domain_sets: self.by_domain_set.len(),
224 }
225 }
226
227 pub fn from_predicates<'a>(predicates: impl IntoIterator<Item = &'a PredicateInfo>) -> Self {
229 let mut matcher = Self::new();
230 for pred in predicates {
231 matcher.add_predicate(pred);
232 }
233 matcher
234 }
235}
236
237#[derive(Clone, Debug, PartialEq, Eq)]
239pub struct MatcherStats {
240 pub total_predicates: usize,
242 pub unique_arities: usize,
244 pub unique_signatures: usize,
246 pub unique_domain_sets: usize,
248}
249
250impl MatcherStats {
251 pub fn avg_index_size(&self) -> f64 {
253 if self.unique_signatures == 0 {
254 0.0
255 } else {
256 self.total_predicates as f64 / self.unique_signatures as f64
257 }
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_add_and_find_by_arity() {
267 let mut matcher = SignatureMatcher::new();
268
269 let knows = PredicateInfo::new("knows", vec!["Person".into(), "Person".into()]);
270 let age = PredicateInfo::new("age", vec!["Person".into()]);
271
272 matcher.add_predicate(&knows);
273 matcher.add_predicate(&age);
274
275 let unary = matcher.find_by_arity(1);
276 assert_eq!(unary.len(), 1);
277 assert!(unary.contains(&"age".to_string()));
278
279 let binary = matcher.find_by_arity(2);
280 assert_eq!(binary.len(), 1);
281 assert!(binary.contains(&"knows".to_string()));
282 }
283
284 #[test]
285 fn test_find_by_exact_signature() {
286 let mut matcher = SignatureMatcher::new();
287
288 let at = PredicateInfo::new("at", vec!["Person".into(), "Location".into()]);
289 matcher.add_predicate(&at);
290
291 let sig = vec!["Person".to_string(), "Location".to_string()];
292 let matches = matcher.find_by_signature(&sig);
293 assert_eq!(matches, vec!["at"]);
294
295 let sig_reversed = vec!["Location".to_string(), "Person".to_string()];
297 let no_matches = matcher.find_by_signature(&sig_reversed);
298 assert!(no_matches.is_empty());
299 }
300
301 #[test]
302 fn test_remove_predicate() {
303 let mut matcher = SignatureMatcher::new();
304
305 let knows = PredicateInfo::new("knows", vec!["Person".into(), "Person".into()]);
306 matcher.add_predicate(&knows);
307
308 assert_eq!(matcher.len(), 1);
309 assert!(matcher.contains("knows"));
310
311 matcher.remove_predicate("knows");
312 assert_eq!(matcher.len(), 0);
313 assert!(!matcher.contains("knows"));
314
315 assert!(matcher.find_by_arity(2).is_empty());
317 }
318
319 #[test]
320 fn test_multiple_predicates_same_signature() {
321 let mut matcher = SignatureMatcher::new();
322
323 let p1 = PredicateInfo::new("pred1", vec!["Person".into(), "Person".into()]);
324 let p2 = PredicateInfo::new("pred2", vec!["Person".into(), "Person".into()]);
325
326 matcher.add_predicate(&p1);
327 matcher.add_predicate(&p2);
328
329 let sig = vec!["Person".to_string(), "Person".to_string()];
330 let matches = matcher.find_by_signature(&sig);
331 assert_eq!(matches.len(), 2);
332 assert!(matches.contains(&"pred1".to_string()));
333 assert!(matches.contains(&"pred2".to_string()));
334 }
335
336 #[test]
337 fn test_from_predicates() {
338 let preds = vec![
339 PredicateInfo::new("knows", vec!["Person".into(), "Person".into()]),
340 PredicateInfo::new("age", vec!["Person".into()]),
341 ];
342
343 let matcher = SignatureMatcher::from_predicates(&preds);
344 assert_eq!(matcher.len(), 2);
345 assert!(matcher.contains("knows"));
346 assert!(matcher.contains("age"));
347 }
348
349 #[test]
350 fn test_stats() {
351 let mut matcher = SignatureMatcher::new();
352
353 matcher.add_predicate(&PredicateInfo::new("p1", vec!["A".into(), "B".into()]));
354 matcher.add_predicate(&PredicateInfo::new("p2", vec!["A".into(), "B".into()]));
355 matcher.add_predicate(&PredicateInfo::new("p3", vec!["C".into()]));
356
357 let stats = matcher.stats();
358 assert_eq!(stats.total_predicates, 3);
359 assert_eq!(stats.unique_arities, 2); assert_eq!(stats.unique_signatures, 2); }
362
363 #[test]
364 fn test_clear() {
365 let mut matcher = SignatureMatcher::new();
366 matcher.add_predicate(&PredicateInfo::new("p1", vec!["A".into()]));
367
368 assert_eq!(matcher.len(), 1);
369 matcher.clear();
370 assert_eq!(matcher.len(), 0);
371 assert!(matcher.is_empty());
372 }
373}