Skip to main content

fukurow_lite/
lib.rs

1//! OWL Lite 推論エンジン
2//!
3//! このクレートは OWL Lite の完全実装を提供します:
4//! - テーブルロー推論アルゴリズム
5//! - 整合性検証
6//! - クラス階層推論
7//! - インスタンス検証
8
9pub mod model;
10pub mod tableau;
11pub mod reasoner;
12pub mod loader;
13
14pub use model::{Ontology, Class, Property, Individual, Axiom};
15pub use reasoner::OwlLiteReasoner;
16pub use loader::OntologyLoader;
17
18// Error types
19use thiserror::Error;
20
21#[derive(Error, Debug)]
22pub enum OwlError {
23    #[error("Loader error: {0}")]
24    LoaderError(String),
25
26    #[error("Reasoning error: {0}")]
27    ReasoningError(String),
28
29    #[error("Consistency error: {0}")]
30    ConsistencyError(String),
31
32    #[error("Unsupported feature: {0}")]
33    UnsupportedFeature(String),
34}
35
36#[cfg(test)]
37fn load_ontology_from_triples(triples: Vec<fukurow_core::model::Triple>) -> Result<crate::model::Ontology, OwlError> {
38    let mut store = fukurow_store::store::RdfStore::new();
39    for triple in triples {
40        store.insert(triple, fukurow_store::provenance::GraphId::Default, fukurow_store::provenance::Provenance::Sensor {
41            source: "test".to_string(),
42            confidence: None,
43        });
44    }
45    let loader = crate::loader::DefaultOntologyLoader;
46    loader.load_from_store(&store)
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use fukurow_core::model::Triple;
53
54    mod loader_tests {
55        use super::*;
56        use crate::model::{Ontology, Class, Property, Individual, Axiom, OwlIri};
57
58        fn create_test_triples() -> Vec<Triple> {
59            vec![
60                // Class declarations
61                Triple {
62                    subject: "http://example.org/Person".to_string(),
63                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
64                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
65                },
66                Triple {
67                    subject: "http://example.org/Animal".to_string(),
68                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
69                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
70                },
71                // Property declarations
72                Triple {
73                    subject: "http://example.org/hasParent".to_string(),
74                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
75                    object: "http://www.w3.org/2002/07/owl#ObjectProperty".to_string(),
76                },
77                Triple {
78                    subject: "http://example.org/hasAge".to_string(),
79                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
80                    object: "http://www.w3.org/2002/07/owl#DatatypeProperty".to_string(),
81                },
82                // Individuals
83                Triple {
84                    subject: "http://example.org/john".to_string(),
85                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
86                    object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
87                },
88                Triple {
89                    subject: "http://example.org/mary".to_string(),
90                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
91                    object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
92                },
93                // Class assertions
94                Triple {
95                    subject: "http://example.org/john".to_string(),
96                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
97                    object: "http://example.org/Person".to_string(),
98                },
99                Triple {
100                    subject: "http://example.org/mary".to_string(),
101                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
102                    object: "http://example.org/Person".to_string(),
103                },
104                // Subclass relation
105                Triple {
106                    subject: "http://example.org/Student".to_string(),
107                    predicate: "http://www.w3.org/2000/01/rdf-schema#subClassOf".to_string(),
108                    object: "http://example.org/Person".to_string(),
109                },
110            ]
111        }
112
113
114        #[test]
115        fn test_load_empty_ontology() {
116            let triples = Vec::new();
117            let ontology = load_ontology_from_triples(triples).unwrap();
118
119            assert!(ontology.classes.is_empty());
120            assert!(ontology.properties.is_empty());
121            assert!(ontology.individuals.is_empty());
122            assert!(ontology.axioms.is_empty());
123        }
124
125        #[test]
126        fn test_load_classes() {
127            let triples = vec![
128                Triple {
129                    subject: "http://example.org/Person".to_string(),
130                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
131                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
132                },
133                Triple {
134                    subject: "http://example.org/Animal".to_string(),
135                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
136                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
137                },
138            ];
139
140            let ontology = load_ontology_from_triples(triples).unwrap();
141
142            assert_eq!(ontology.classes.len(), 2);
143            assert!(ontology.classes.contains(&Class::Named(OwlIri::new("http://example.org/Person".to_string()))));
144            assert!(ontology.classes.contains(&Class::Named(OwlIri::new("http://example.org/Animal".to_string()))));
145        }
146
147        #[test]
148        fn test_load_properties() {
149            let triples = vec![
150                Triple {
151                    subject: "http://example.org/hasParent".to_string(),
152                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
153                    object: "http://www.w3.org/2002/07/owl#ObjectProperty".to_string(),
154                },
155                Triple {
156                    subject: "http://example.org/hasAge".to_string(),
157                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
158                    object: "http://www.w3.org/2002/07/owl#DatatypeProperty".to_string(),
159                },
160            ];
161
162            let ontology = load_ontology_from_triples(triples).unwrap();
163
164            assert_eq!(ontology.properties.len(), 2);
165            assert!(ontology.properties.contains(&Property::Object(OwlIri::new("http://example.org/hasParent".to_string()))));
166            assert!(ontology.properties.contains(&Property::Data(OwlIri::new("http://example.org/hasAge".to_string()))));
167        }
168
169        #[test]
170        fn test_load_individuals() {
171            let triples = vec![
172                Triple {
173                    subject: "http://example.org/john".to_string(),
174                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
175                    object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
176                },
177                Triple {
178                    subject: "http://example.org/mary".to_string(),
179                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
180                    object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
181                },
182            ];
183
184            let ontology = load_ontology_from_triples(triples).unwrap();
185
186            assert_eq!(ontology.individuals.len(), 2);
187            assert!(ontology.individuals.contains(&Individual(OwlIri::new("http://example.org/john".to_string()))));
188            assert!(ontology.individuals.contains(&Individual(OwlIri::new("http://example.org/mary".to_string()))));
189        }
190
191        #[test]
192        fn test_load_class_assertions() {
193            let triples = vec![
194                // Named individual
195                Triple {
196                    subject: "http://example.org/john".to_string(),
197                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
198                    object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
199                },
200                // Class
201                Triple {
202                    subject: "http://example.org/Person".to_string(),
203                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
204                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
205                },
206                // Class assertion
207                Triple {
208                    subject: "http://example.org/john".to_string(),
209                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
210                    object: "http://example.org/Person".to_string(),
211                },
212            ];
213
214            let ontology = load_ontology_from_triples(triples).unwrap();
215
216            assert_eq!(ontology.classes.len(), 1);
217            assert_eq!(ontology.individuals.len(), 1);
218            assert_eq!(ontology.axioms.len(), 1);
219
220            // Check for class assertion axiom
221            match &ontology.axioms[0] {
222                Axiom::ClassAssertion(class, individual) => {
223                    assert_eq!(*class, Class::Named(OwlIri::new("http://example.org/Person".to_string())));
224                    assert_eq!(*individual, Individual(OwlIri::new("http://example.org/john".to_string())));
225                }
226                _ => panic!("Expected ClassAssertion axiom"),
227            }
228        }
229
230        #[test]
231        fn test_load_subclass_relation() {
232            let triples = vec![
233                // Classes
234                Triple {
235                    subject: "http://example.org/Person".to_string(),
236                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
237                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
238                },
239                Triple {
240                    subject: "http://example.org/Student".to_string(),
241                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
242                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
243                },
244                // Subclass relation
245                Triple {
246                    subject: "http://example.org/Student".to_string(),
247                    predicate: "http://www.w3.org/2000/01/rdf-schema#subClassOf".to_string(),
248                    object: "http://example.org/Person".to_string(),
249                },
250            ];
251
252            let ontology = load_ontology_from_triples(triples).unwrap();
253
254            assert_eq!(ontology.classes.len(), 2);
255            assert_eq!(ontology.axioms.len(), 1);
256
257            // Check for subclass axiom
258            match &ontology.axioms[0] {
259                Axiom::SubClassOf(subclass, superclass) => {
260                    assert_eq!(*subclass, Class::Named(OwlIri::new("http://example.org/Student".to_string())));
261                    assert_eq!(*superclass, Class::Named(OwlIri::new("http://example.org/Person".to_string())));
262                }
263                _ => panic!("Expected SubClassOf axiom"),
264            }
265        }
266
267        #[test]
268        fn test_load_complex_ontology() {
269            let triples = create_test_triples();
270
271            let ontology = load_ontology_from_triples(triples).unwrap();
272
273            assert_eq!(ontology.classes.len(), 3); // Person, Animal, Student
274            assert_eq!(ontology.properties.len(), 2); // hasParent, hasAge
275            assert_eq!(ontology.individuals.len(), 2); // john, mary
276            assert_eq!(ontology.axioms.len(), 3); // 2 class assertions + 1 subclass
277        }
278    }
279
280    mod reasoner_tests {
281        use super::*;
282        use crate::reasoner::OwlLiteReasoner;
283        use crate::model::{Class, Individual, OwlIri};
284
285        #[test]
286        fn test_reasoner_creation() {
287            let reasoner = OwlLiteReasoner::new();
288            // Basic functionality test
289            assert!(true); // Just ensure it can be created
290        }
291
292        #[test]
293        fn test_consistency_check() {
294            let reasoner = OwlLiteReasoner::new();
295
296            // Create a simple consistent ontology
297            let triples = vec![
298                Triple {
299                    subject: "http://example.org/Person".to_string(),
300                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
301                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
302                },
303                Triple {
304                    subject: "http://example.org/john".to_string(),
305                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
306                    object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
307                },
308                Triple {
309                    subject: "http://example.org/john".to_string(),
310                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
311                    object: "http://example.org/Person".to_string(),
312                },
313            ];
314
315            let ontology = load_ontology_from_triples(triples).unwrap();
316            let mut reasoner = OwlLiteReasoner::new();
317            let result = reasoner.is_consistent(&ontology);
318
319            // For now, assume consistency check passes for simple ontologies
320            assert!(result.unwrap_or(false));
321        }
322
323        #[test]
324        fn test_class_hierarchy_inference() {
325            let reasoner = OwlLiteReasoner::new();
326
327            let triples = vec![
328                // Classes
329                Triple {
330                    subject: "http://example.org/Person".to_string(),
331                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
332                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
333                },
334                Triple {
335                    subject: "http://example.org/Student".to_string(),
336                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
337                    object: "http://www.w3.org/2002/07/owl#Class".to_string(),
338                },
339                // Subclass
340                Triple {
341                    subject: "http://example.org/Student".to_string(),
342                    predicate: "http://www.w3.org/2000/01/rdf-schema#subClassOf".to_string(),
343                    object: "http://example.org/Person".to_string(),
344                },
345                // Individual
346                Triple {
347                    subject: "http://example.org/john".to_string(),
348                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
349                    object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
350                },
351                // Instance of Student
352                Triple {
353                    subject: "http://example.org/john".to_string(),
354                    predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
355                    object: "http://example.org/Student".to_string(),
356                },
357            ];
358
359            let ontology = load_ontology_from_triples(triples).unwrap();
360            let mut reasoner = OwlLiteReasoner::new();
361            let inferred = reasoner.get_inferred_axioms(&ontology).unwrap();
362
363            // Check that john is also inferred to be a Person
364            let john_person_assertion = inferred.iter().any(|axiom| {
365                matches!(axiom, Axiom::ClassAssertion(class, individual)
366                    if *class == Class::Named(OwlIri::new("http://example.org/Person".to_string()))
367                    && *individual == Individual(OwlIri::new("http://example.org/john".to_string())))
368            });
369
370            assert!(john_person_assertion, "john should be inferred to be a Person");
371        }
372    }
373
374    mod model_tests {
375        use super::*;
376        use crate::model::{Ontology, Class, Property, Individual, OwlIri};
377
378        #[test]
379        fn test_ontology_creation() {
380            let ontology = Ontology::new();
381            assert!(ontology.classes.is_empty());
382            assert!(ontology.properties.is_empty());
383            assert!(ontology.individuals.is_empty());
384            assert!(ontology.axioms.is_empty());
385        }
386
387        #[test]
388        fn test_class_equality() {
389            let class1 = Class::Named(OwlIri::new("http://example.org/Person".to_string()));
390            let class2 = Class::Named(OwlIri::new("http://example.org/Person".to_string()));
391            let class3 = Class::Named(OwlIri::new("http://example.org/Animal".to_string()));
392
393            assert_eq!(class1, class2);
394            assert_ne!(class1, class3);
395        }
396
397        #[test]
398        fn test_individual_equality() {
399            let ind1 = Individual(OwlIri::new("http://example.org/john".to_string()));
400            let ind2 = Individual(OwlIri::new("http://example.org/john".to_string()));
401            let ind3 = Individual(OwlIri::new("http://example.org/mary".to_string()));
402
403            assert_eq!(ind1, ind2);
404            assert_ne!(ind1, ind3);
405        }
406
407        #[test]
408        fn test_property_equality() {
409            let prop1 = Property::Object(OwlIri::new("http://example.org/hasParent".to_string()));
410            let prop2 = Property::Object(OwlIri::new("http://example.org/hasParent".to_string()));
411            let prop3 = Property::Data(OwlIri::new("http://example.org/hasAge".to_string()));
412
413            assert_eq!(prop1, prop2);
414            assert_ne!(prop1, prop3);
415        }
416
417        #[test]
418        fn test_owl_iri_creation() {
419            let iri = OwlIri::new("http://example.org/Person".to_string());
420            assert_eq!(iri.0, "http://example.org/Person");
421        }
422    }
423}