A reasoner for OWL-DL ontologies.
The reasoner is initialized with a theory, which can be loaded from a file or built up programmatically.
Let's start with a simple data model. Note our data model must be heavily normalized to be visible
to OWL-DL. All individuals are represented as subclasses of Thing. These can't (yet) have properties
of their own, these must be represented as separate relationships, which are subclasses of TopObjectProperty.
Our model will have a class for representing people, and a relationship for representing who knows who.
>>> class Person(Thing):
... pass
>>> class Knows(TopObjectProperty):
... domain = Person
... range = Person
... symmetric = True
We can now create an OWL reasoner:
>>> from typedlogic.integrations.frameworks.owldl.reasoner import OWLReasoner
>>> r = OWLReasoner()
>>> r.init_axioms([Person, Knows])
And add some facts. These are standard python objects:
>>> r.add(Knows("p1", "p2"))
We can now reason with the model:
>>> assert r.coherent()
>>> model = r.model()
>>> for fact in model.iter_retrieve("Knows"):
... print(fact)
Knows(p1, p2)
Knows(p2, p1)
Note that because we declared the relationship as symmetric, the reasoner inferred the reverse relationship.
By default, the reasoner uses the Clingo solver. This can be changed by setting the solver_class
attribute.
>>> from typedlogic.integrations.solvers.souffle import SouffleSolver
>>> r = OWLReasoner(solver_class=SouffleSolver)
>>> r.init_axioms([Person, Knows])
>>> r.add(Knows("p1", "p2"))
>>> assert r.coherent()
>>> model = r.model()
>>> for fact in model.iter_retrieve("Knows"):
... print(fact)
Knows(p1, p2)
Knows(p2, p1)
Source code in src/typedlogic/integrations/frameworks/owldl/reasoner.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 | @dataclass
class OWLReasoner:
"""
A reasoner for OWL-DL ontologies.
The reasoner is initialized with a theory, which can be loaded from a file or built up programmatically.
Let's start with a simple data model. Note our data model must be heavily *normalized* to be visible
to OWL-DL. All individuals are represented as subclasses of Thing. These can't (yet) have properties
of their own, these must be represented as separate relationships, which are subclasses of TopObjectProperty.
Our model will have a class for representing people, and a relationship for representing who knows who.
>>> class Person(Thing):
... pass
>>> class Knows(TopObjectProperty):
... domain = Person
... range = Person
... symmetric = True
We can now create an OWL reasoner:
>>> from typedlogic.integrations.frameworks.owldl.reasoner import OWLReasoner
>>> r = OWLReasoner()
>>> r.init_axioms([Person, Knows])
And add some facts. These are standard python objects:
>>> r.add(Knows("p1", "p2"))
We can now reason with the model:
>>> assert r.coherent()
>>> model = r.model()
>>> for fact in model.iter_retrieve("Knows"):
... print(fact)
Knows(p1, p2)
Knows(p2, p1)
Note that because we declared the relationship as symmetric, the reasoner inferred the reverse relationship.
By default, the reasoner uses the Clingo solver. This can be changed by setting the `solver_class` attribute.
>>> from typedlogic.integrations.solvers.souffle import SouffleSolver
>>> r = OWLReasoner(solver_class=SouffleSolver)
>>> r.init_axioms([Person, Knows])
>>> r.add(Knows("p1", "p2"))
>>> assert r.coherent()
>>> model = r.model()
>>> for fact in model.iter_retrieve("Knows"):
... print(fact)
Knows(p1, p2)
Knows(p2, p1)
"""
solver_class: Type[Solver] = ClingoSolver
theory: Optional[Theory] = None
solver: Optional[Solver] = None
def set_solver_class(self, solver_class: Type[Solver]):
self.solver_class = solver_class
self.solver = None
def init_from_file(self, source: Union[str, Path]):
p = OWLPyParser()
self.theory = p.parse(source)
def init_axioms(self, classes: Optional[List[Type]] = None):
"""
Initializes axioms from what is currently loaded.
:return:
"""
import typedlogic.integrations.frameworks.owldl.owltop as owltop
p = OWLPyParser()
modules = None
if classes:
modules = []
for cls in classes:
module = inspect.getmodule(cls)
if module:
modules.append(module)
modules = list(set(modules))
self.theory = p.parse(owltop.__file__, include_all=True, modules=modules)
if classes:
for cls in classes:
pd = PredicateDefinition(cls.__name__, introspect_attributes(cls))
self.theory.predicate_definitions.append(pd)
def add(self, sentence: Union[Sentence, List[Sentence]]):
"""
Add a sentence to the reasoner
:param sentence:
:return:
"""
self.solver = None # solver's state is invalidated
if isinstance(sentence, list):
for s in sentence:
if s is None:
raise ValueError(f"Got empty sentence in {sentence}")
self.add(s)
return
if self.theory is None:
self.theory = Theory()
self.theory.add(sentence)
def register(self, cls: Type):
"""
Register a python class with the reasoner
:param cls:
:return:
"""
if not self.theory:
self.theory = Theory()
pd_map = {pd.predicate: pd for pd in self.theory.predicate_definitions or []}
pd = None
for mc in [Thing, TopObjectProperty, TopDataProperty]:
if issubclass(cls, mc):
pd = PredicateDefinition(cls.__name__, pd_map[mc.__name__].arguments)
if not pd:
raise ValueError(f"Class {cls} is not a recognized OWL-DL class")
self.theory.predicate_definitions.append(pd)
sentences = cls.to_sentences()
for s in sentences:
self.add(s)
def remove(self, sentence: Union[Sentence, List[Sentence]]):
self.solver = None # solver's state is invalidated
if isinstance(sentence, list):
for s in sentence:
self.remove(s)
return
if self.theory is None:
raise ValueError("No theory to remove from")
self.theory.remove(sentence)
def reason(self) -> None:
self.solver = self.solver_class()
if not self.theory:
raise ValueError("No theory to reason with")
self.solver.add_theory(self.theory)
def model(self) -> Model:
if not self.solver:
self.reason()
if not self.solver:
raise ValueError("No solver to reason with")
solver = self.solver
return solver.model()
def model_iter(self) -> Iterator[Model]:
if not self.solver:
self.reason()
if not self.solver:
raise ValueError("No solver to reason with")
solver = self.solver
yield from solver.models()
def coherent(self) -> bool:
if not self.solver:
self.reason()
if not self.solver:
raise ValueError("No solver to reason with")
solver = self.solver
return solver.check().satisfiable is not False
|
init_axioms(classes=None)
Initializes axioms from what is currently loaded.
Returns:
Source code in src/typedlogic/integrations/frameworks/owldl/reasoner.py
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 | def init_axioms(self, classes: Optional[List[Type]] = None):
"""
Initializes axioms from what is currently loaded.
:return:
"""
import typedlogic.integrations.frameworks.owldl.owltop as owltop
p = OWLPyParser()
modules = None
if classes:
modules = []
for cls in classes:
module = inspect.getmodule(cls)
if module:
modules.append(module)
modules = list(set(modules))
self.theory = p.parse(owltop.__file__, include_all=True, modules=modules)
if classes:
for cls in classes:
pd = PredicateDefinition(cls.__name__, introspect_attributes(cls))
self.theory.predicate_definitions.append(pd)
|
add(sentence)
Add a sentence to the reasoner
Parameters:
Returns:
Source code in src/typedlogic/integrations/frameworks/owldl/reasoner.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 | def add(self, sentence: Union[Sentence, List[Sentence]]):
"""
Add a sentence to the reasoner
:param sentence:
:return:
"""
self.solver = None # solver's state is invalidated
if isinstance(sentence, list):
for s in sentence:
if s is None:
raise ValueError(f"Got empty sentence in {sentence}")
self.add(s)
return
if self.theory is None:
self.theory = Theory()
self.theory.add(sentence)
|
register(cls)
Register a python class with the reasoner
Parameters:
Name |
Type |
Description |
Default |
cls
|
Type
|
|
required
|
Returns:
Source code in src/typedlogic/integrations/frameworks/owldl/reasoner.py
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 | def register(self, cls: Type):
"""
Register a python class with the reasoner
:param cls:
:return:
"""
if not self.theory:
self.theory = Theory()
pd_map = {pd.predicate: pd for pd in self.theory.predicate_definitions or []}
pd = None
for mc in [Thing, TopObjectProperty, TopDataProperty]:
if issubclass(cls, mc):
pd = PredicateDefinition(cls.__name__, pd_map[mc.__name__].arguments)
if not pd:
raise ValueError(f"Class {cls} is not a recognized OWL-DL class")
self.theory.predicate_definitions.append(pd)
sentences = cls.to_sentences()
for s in sentences:
self.add(s)
|