activerdf_rules 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.txt +2 -0
- data/Manifest.txt +10 -3
- data/README.txt +3 -8
- data/TODO.txt +1 -0
- data/lib/activerdf_rules/rule_base.rb +21 -16
- data/lib/activerdf_rules/rule_engine.rb +180 -52
- data/lib/activerdf_rules/rule_engine_mixin.rb +8 -4
- data/lib/activerdf_rules/version.rb +1 -1
- data/test/activerdf_rules/{owl_rulebase_test.rb → owl_rule_base_test.rb} +6 -2
- data/test/activerdf_rules/rdfs_ext_rule_base_test.rb +127 -0
- data/test/activerdf_rules/rdfs_rule_base_test.rb +183 -0
- data/test/activerdf_rules/rule_engine_test.rb +14 -9
- metadata +15 -7
- data/setup.rb +0 -1585
- data/test/activerdf_rules/rdfs_rulebase_test.rb +0 -95
data/CHANGELOG.txt
ADDED
data/Manifest.txt
CHANGED
@@ -1,13 +1,20 @@
|
|
1
|
+
CHANGELOG.txt
|
1
2
|
History.txt
|
2
3
|
Manifest.txt
|
3
4
|
README.txt
|
4
5
|
Rakefile
|
5
|
-
|
6
|
+
TODO.txt
|
6
7
|
examples/person_example.rb
|
7
8
|
lib/activerdf_rules.rb
|
8
9
|
lib/activerdf_rules/rule_base.rb
|
9
|
-
lib/activerdf_rules/rule_engine_mixin.rb
|
10
10
|
lib/activerdf_rules/rule_engine.rb
|
11
|
+
lib/activerdf_rules/rule_engine_mixin.rb
|
11
12
|
lib/activerdf_rules/version.rb
|
12
|
-
test/
|
13
|
+
test/activerdf_rules/owl_rule_base_test.rb
|
14
|
+
test/activerdf_rules/rdfs_ext_rule_base_test.rb
|
15
|
+
test/activerdf_rules/rdfs_rule_base_test.rb
|
16
|
+
test/activerdf_rules/rule_base_test.rb
|
17
|
+
test/activerdf_rules/rule_engine_mixin_test.rb
|
18
|
+
test/activerdf_rules/rule_engine_test.rb
|
13
19
|
test/activerdf_rules_test.rb
|
20
|
+
test/test_helper.rb
|
data/README.txt
CHANGED
@@ -2,22 +2,17 @@
|
|
2
2
|
ActiveRDF Rules is a rule base and forward chaining production system for
|
3
3
|
ActiveRDF. It can be used to process rules programatically at will, or using a
|
4
4
|
mix-in class you can have it process rules every time a triple is added to the
|
5
|
-
data store. The rules are
|
5
|
+
data store. The rules are stored in memory, and can be defined using a DSL. It comes
|
6
6
|
with a couple of prebuilt rule bases for RDF Schema and OWL reasoning.
|
7
7
|
|
8
8
|
=What it is not?
|
9
9
|
* It is not high performance.
|
10
10
|
* The rules are stored in memory, so don't add a lot of them.
|
11
|
-
* It supports *some* basic
|
11
|
+
* It supports RDF Schema and *some* basic OWL reasoning through the built-in
|
12
12
|
rule bases, but they are *basic*. Feel free to not use them and just define
|
13
13
|
your own.
|
14
14
|
* It does not support restrictions. I'm probably going to add restriction
|
15
|
-
rules at some point, but I haven't thought through it all just yet.
|
16
|
-
not sure how to handle some cases. Right now, for instance, if a property has
|
17
|
-
a domain specified, then it will conclude that any object that has that
|
18
|
-
property must be of the type specified as the domain, as opposed to restricting
|
19
|
-
only that type to be able to have that property. That's the way I was told to
|
20
|
-
do it!
|
15
|
+
rules at some point, but I haven't thought through it all just yet.
|
21
16
|
* It is not stable. I mean it's stable; I have tested it pretty thoroghly as
|
22
17
|
far as its current features go, but I reserve the right to change the
|
23
18
|
implementation, interface, and anything else. I might even change the name of
|
data/TODO.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Add the RDF Semantics Extensional rules
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# base.
|
4
4
|
#
|
5
5
|
# Example:
|
6
|
-
# rb = RuleBase.new do
|
6
|
+
# rb = RuleBase.new 'DSL RuleBase' do
|
7
7
|
# rule 'Rule1' do
|
8
8
|
# condition :x, :y, :z
|
9
9
|
#
|
@@ -21,7 +21,7 @@
|
|
21
21
|
# the +rules+ array manually
|
22
22
|
#
|
23
23
|
# Example:
|
24
|
-
# rb = RuleBase.new '
|
24
|
+
# rb = RuleBase.new 'Manual RuleBase'
|
25
25
|
# r = RuleBase::Rule.new 'Rule 1'
|
26
26
|
# r.condition :x, :y, :z
|
27
27
|
# r.conclusion :z, :y, :x
|
@@ -85,7 +85,7 @@ class RuleBase
|
|
85
85
|
# the conditions are satisfied, then the conclusion is introduced into the
|
86
86
|
# database substituting for any variables that may appear in the conclusion.
|
87
87
|
#
|
88
|
-
# Variables are simply
|
88
|
+
# Variables are simply ruby symbols and they can be used within both the
|
89
89
|
# conditions and the conclusion with the following caveat: any variable that
|
90
90
|
# appears in the conclusion must also appear in at least one condition.
|
91
91
|
#
|
@@ -154,19 +154,24 @@ class RuleBase
|
|
154
154
|
|
155
155
|
change = false
|
156
156
|
q.execute.each do |row|
|
157
|
-
triple =
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
157
|
+
triple = nil
|
158
|
+
begin
|
159
|
+
triple = @conclusion.collect {|t|
|
160
|
+
instantiate(t, row, lookup)
|
161
|
+
}
|
162
|
+
old = ConnectionPool.write_adapter.size
|
163
|
+
$activerdflog.debug("concluding #{triple.inspect}")
|
164
|
+
ConnectionPool.write_adapter.add(*triple)
|
165
|
+
# TODO HACK This should be changed
|
166
|
+
# What I should be able to do is something like below, but the below is
|
167
|
+
# broken for redland, and the above condition query is broken in
|
168
|
+
# rdflite (because of ambiguous column names). For now this works.
|
169
|
+
# Query.new.ask.where(*triple).execute
|
170
|
+
if old < ConnectionPool.write_adapter.size
|
171
|
+
change = true
|
172
|
+
end
|
173
|
+
rescue => e
|
174
|
+
$activerdflog.debug("could not add #{triple.inspect} because of #{e}")
|
170
175
|
end
|
171
176
|
end
|
172
177
|
change
|
@@ -1,44 +1,40 @@
|
|
1
|
-
# A RuleEngine will use a
|
2
|
-
# by processing the rules.
|
3
|
-
# TODO
|
1
|
+
# A RuleEngine will use a collection of RuleBases to add new data to an
|
2
|
+
# ActiveRDF data store by processing the rules.
|
4
3
|
class RuleEngine
|
5
|
-
|
4
|
+
attr_reader :rule_bases
|
6
5
|
|
7
6
|
# A RuleBase can be specified using the hash key :rule_base.
|
8
7
|
#
|
9
8
|
# Example:
|
10
9
|
# RuleEngine.new :rule_base => RuleEngine::RDFSRuleBase
|
11
10
|
def initialize(values = {})
|
12
|
-
@
|
11
|
+
@rule_bases = []
|
12
|
+
@rule_bases << values[:rule_base] if values[:rule_base]
|
13
13
|
end
|
14
14
|
|
15
|
+
# Creates a new RuleBase, adds it to the array of rule bases, and passes the
|
16
|
+
# block to the RuleBase constructor. This allows use of the RuleBase DSL.
|
15
17
|
def define_rules(&b)
|
16
18
|
rb = RuleBase.new(&b)
|
17
|
-
self.
|
18
|
-
end
|
19
|
-
|
20
|
-
def rule_base=(rb)
|
21
|
-
if @rule_base.nil?
|
22
|
-
@rule_base = rb.dup
|
23
|
-
else
|
24
|
-
if rb.nil?
|
25
|
-
@rule_base = nil
|
26
|
-
else
|
27
|
-
@rule_base.rules.push(*rb.rules)
|
28
|
-
end
|
29
|
-
end
|
19
|
+
self.rule_bases << rb
|
30
20
|
end
|
31
21
|
|
22
|
+
# Loops through all of the rule bases and processes each rule in each rule
|
23
|
+
# base. Returns true if any of the rules have added information to the
|
24
|
+
# database.
|
32
25
|
def process_rules
|
33
26
|
$activerdflog.debug("processing '#{@rule_base}'")
|
34
27
|
change = false
|
35
|
-
@
|
36
|
-
|
28
|
+
@rule_bases.each do |rb|
|
29
|
+
rb.rules.each do |r|
|
30
|
+
change |= r.process
|
31
|
+
end
|
37
32
|
end
|
38
33
|
change
|
39
34
|
end
|
40
35
|
|
41
|
-
|
36
|
+
# A rule base including rules for RDFS reasoning.
|
37
|
+
RDFSRuleBase = RuleBase.new('RDFSRuleBase') {
|
42
38
|
type = Namespace.lookup(:rdf, 'type')
|
43
39
|
rdfclass = Namespace.lookup(:rdfs, 'Class')
|
44
40
|
subClassOf = Namespace.lookup(:rdfs, 'subClassOf')
|
@@ -46,64 +42,188 @@ class RuleEngine
|
|
46
42
|
subPropertyOf = Namespace.lookup(:rdfs, 'subPropertyOf')
|
47
43
|
range = Namespace.lookup(:rdfs, 'range')
|
48
44
|
domain = Namespace.lookup(:rdfs, 'domain')
|
45
|
+
resource = Namespace.lookup(:rdfs, 'Resource')
|
46
|
+
container = Namespace.lookup(:rdfs, 'ContainerMembershipProperty')
|
47
|
+
member = Namespace.lookup(:rdfs, 'member')
|
48
|
+
datatype = Namespace.lookup(:rdfs, 'Datatype')
|
49
|
+
literal = Namespace.lookup(:rdfs, 'Literal')
|
49
50
|
|
50
|
-
# The following reasoning rules
|
51
|
-
# http://www
|
51
|
+
# The following reasoning rules were taken from the RDF Semantics document
|
52
|
+
# http://www.w3.org/TR/rdf-mt/#RDFRules
|
53
|
+
# http://www.w3.org/TR/rdf-mt/#RDFSRules
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
condition :Morris, type, :Cat
|
56
|
-
condition :Cat, subClassOf, :Mammal
|
55
|
+
rule "rdf1" do
|
56
|
+
condition :s, :p, :o
|
57
57
|
|
58
|
-
conclusion :
|
58
|
+
conclusion :p, type, property
|
59
59
|
end
|
60
60
|
|
61
|
-
|
62
|
-
|
61
|
+
rule 'rdfs2' do
|
62
|
+
condition :teaches, domain, :Teacher
|
63
|
+
condition :Bob, :teaches, :Scooter
|
64
|
+
|
65
|
+
conclusion :Bob, type, :Teacher
|
66
|
+
end
|
67
|
+
|
68
|
+
rule 'rdfs3' do
|
69
|
+
condition :teaches, range, :Student
|
70
|
+
condition :Bob, :teaches, :Scooter
|
71
|
+
|
72
|
+
conclusion :Scooter, type, :Student
|
73
|
+
end
|
74
|
+
|
75
|
+
rule 'rdfs4a' do
|
76
|
+
condition :s, :p, :o
|
77
|
+
|
78
|
+
conclusion :s, type, resource
|
79
|
+
end
|
80
|
+
|
81
|
+
rule 'rdfs4b' do
|
82
|
+
condition :s, :p, :o
|
83
|
+
|
84
|
+
conclusion :o, type, resource
|
85
|
+
end
|
86
|
+
|
87
|
+
rule 'rdfs5' do
|
88
|
+
condition :parent, subPropertyOf, :ancestor
|
89
|
+
condition :ancestor, subPropertyOf, :relative
|
90
|
+
|
91
|
+
conclusion :parent, subPropertyOf, :relative
|
92
|
+
end
|
93
|
+
|
94
|
+
rule 'rdfs6' do
|
63
95
|
condition :p, type, property
|
64
96
|
|
65
97
|
conclusion :p, subPropertyOf, :p
|
66
98
|
end
|
67
99
|
|
68
|
-
rule do
|
69
|
-
condition :
|
100
|
+
rule 'rdfs7' do
|
101
|
+
condition :parent, subPropertyOf, :ancestor
|
102
|
+
condition :Paul, :parent, :Lester
|
70
103
|
|
71
|
-
conclusion :
|
104
|
+
conclusion :Paul, :ancestor, :Lester
|
72
105
|
end
|
73
106
|
|
74
|
-
|
75
|
-
|
76
|
-
condition :teaches, domain, :Teacher
|
77
|
-
condition :Bob, :teaches, :Scooter
|
107
|
+
rule 'rdfs8' do
|
108
|
+
condition :c, type, rdfclass
|
78
109
|
|
79
|
-
conclusion :
|
110
|
+
conclusion :c, subClassOf, resource
|
80
111
|
end
|
81
112
|
|
82
|
-
rule do
|
83
|
-
condition :
|
84
|
-
condition :
|
113
|
+
rule 'rdfs9' do
|
114
|
+
condition :Morris, type, :Cat
|
115
|
+
condition :Cat, subClassOf, :Mammal
|
85
116
|
|
86
|
-
conclusion :
|
117
|
+
conclusion :Morris, type, :Mammal
|
87
118
|
end
|
88
119
|
|
89
|
-
|
90
|
-
|
120
|
+
rule 'rdfs10' do
|
121
|
+
condition :p, type, rdfclass
|
122
|
+
|
123
|
+
conclusion :p, subClassOf, :p
|
124
|
+
end
|
125
|
+
|
126
|
+
rule 'rdfs11' do
|
91
127
|
condition :Dog, subClassOf, :Mammal
|
92
128
|
condition :Mammal, subClassOf, :Animal
|
93
129
|
|
94
130
|
conclusion :Dog, subClassOf, :Animal
|
95
131
|
end
|
96
132
|
|
97
|
-
rule do
|
98
|
-
condition :
|
99
|
-
condition :ancestor, subPropertyOf, :relative
|
133
|
+
rule 'rdfs12' do
|
134
|
+
condition :p, type, container
|
100
135
|
|
101
|
-
conclusion :
|
136
|
+
conclusion :p, subClassOf, member
|
102
137
|
end
|
103
|
-
|
104
|
-
|
138
|
+
|
139
|
+
rule 'rdfs13' do
|
140
|
+
condition :p, type, datatype
|
141
|
+
|
142
|
+
conclusion :p, subClassOf, literal
|
143
|
+
end
|
144
|
+
}
|
145
|
+
|
146
|
+
# A rule base including rules for "stronger extensional semantic conditions"
|
147
|
+
# according to the RDF Semantics document.
|
148
|
+
RDFSExtRuleBase = RuleBase.new('RDFSExtRuleBase') {
|
149
|
+
domain = Namespace.lookup(:rdfs, 'domain')
|
150
|
+
subClassOf = Namespace.lookup(:rdfs, 'subClassOf')
|
151
|
+
range = Namespace.lookup(:rdfs, 'range')
|
152
|
+
subPropertyOf = Namespace.lookup(:rdfs, 'subPropertyOf')
|
153
|
+
type = Namespace.lookup(:rdf, 'type')
|
154
|
+
resource = Namespace.lookup(:rdfs, 'Resource')
|
155
|
+
property = Namespace.lookup(:rdf, 'Property')
|
156
|
+
rdfsclass = Namespace.lookup(:rdfs, 'Class')
|
157
|
+
|
158
|
+
# The following reasoning rules were taken from the RDF Semantics document
|
159
|
+
# http://www.w3.org/TR/rdf-mt/#RDFSExtRules
|
160
|
+
|
161
|
+
rule 'ext1' do
|
162
|
+
condition :uuu, domain, :vvv
|
163
|
+
condition :vvv, subClassOf, :zzz
|
164
|
+
|
165
|
+
conclusion :uuu, domain, :zzz
|
166
|
+
end
|
167
|
+
|
168
|
+
rule 'ext2' do
|
169
|
+
condition :uuu, range, :vvv
|
170
|
+
condition :vvv, subClassOf, :zzz
|
171
|
+
|
172
|
+
conclusion :uuu, range, :zzz
|
173
|
+
end
|
174
|
+
|
175
|
+
rule 'ext3' do
|
176
|
+
condition :uuu, domain, :vvv
|
177
|
+
condition :www, subPropertyOf, :uuu
|
178
|
+
|
179
|
+
conclusion :www, domain, :vvv
|
180
|
+
end
|
181
|
+
|
182
|
+
rule 'ext4' do
|
183
|
+
condition :uuu, range, :vvv
|
184
|
+
condition :www, subPropertyOf, :uuu
|
185
|
+
|
186
|
+
conclusion :www, range, :vvv
|
187
|
+
end
|
188
|
+
|
189
|
+
rule 'ext5' do
|
190
|
+
condition type, subPropertyOf, :www
|
191
|
+
condition :www, domain, :vvv
|
192
|
+
|
193
|
+
conclusion resource, subClassOf, :vvv
|
194
|
+
end
|
195
|
+
|
196
|
+
rule 'ext6' do
|
197
|
+
condition subClassOf, subPropertyOf, :www
|
198
|
+
condition :www, domain, :vvv
|
199
|
+
|
200
|
+
conclusion rdfsclass, subClassOf, :vvv
|
201
|
+
end
|
202
|
+
|
203
|
+
rule 'ext7' do
|
204
|
+
condition subPropertyOf, subPropertyOf, :www
|
205
|
+
condition :www, domain, :vvv
|
206
|
+
|
207
|
+
conclusion property, subClassOf, :vvv
|
208
|
+
end
|
209
|
+
|
210
|
+
rule 'ext8' do
|
211
|
+
condition subClassOf, subPropertyOf, :www
|
212
|
+
condition :www, range, :vvv
|
213
|
+
|
214
|
+
conclusion rdfsclass, subClassOf, :vvv
|
215
|
+
end
|
216
|
+
|
217
|
+
rule 'ext9' do
|
218
|
+
condition subPropertyOf, subPropertyOf, :www
|
219
|
+
condition :www, range, :vvv
|
220
|
+
|
221
|
+
conclusion property, subClassOf, :vvv
|
222
|
+
end
|
223
|
+
}
|
105
224
|
|
106
|
-
|
225
|
+
# A preliminary (alpha) rule base for OWL reasoning.
|
226
|
+
OWLRuleBase = RuleBase.new('OWLRuleBase') {
|
107
227
|
type = Namespace.lookup(:rdf, 'type')
|
108
228
|
transProp = Namespace.lookup(:owl, 'TransitiveProperty')
|
109
229
|
symProp = Namespace.lookup(:owl, 'SymmetricProperty')
|
@@ -262,6 +382,14 @@ class RuleEngine
|
|
262
382
|
# owl:TransitiveProperty, then Q is also an owl:TransitiveProperty.
|
263
383
|
|
264
384
|
# TODO All of the elements of an owl:AllDifferent are owl:differentFrom each other.
|
265
|
-
|
385
|
+
}
|
386
|
+
|
387
|
+
RDFSRuleBase.freeze
|
388
|
+
RDFSRuleBase.rules.freeze
|
389
|
+
|
390
|
+
RDFSExtRuleBase.freeze
|
391
|
+
RDFSExtRuleBase.rules.freeze
|
392
|
+
|
393
|
+
OWLRuleBase.freeze
|
266
394
|
OWLRuleBase.rules.freeze
|
267
395
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# This module may be included in any writeable ActiveRDF adapter in order to
|
2
|
-
# have automatic rule processing every time a triple is added to the database
|
2
|
+
# have automatic rule processing every time a triple is added to the database,
|
3
|
+
# and it will continue to process the rules until none of the rules will add
|
4
|
+
# new information to the database.
|
3
5
|
#
|
4
6
|
# Example:
|
5
7
|
# class << adapter
|
@@ -11,15 +13,17 @@
|
|
11
13
|
# mixed in. If you do not specify a RuleEngine, then automatic rule processing
|
12
14
|
# will NOT take place.
|
13
15
|
#
|
14
|
-
# There are
|
16
|
+
# There are three included RuleBases two for RDF Schema, and one for OWL.
|
15
17
|
module RuleEngineMixin
|
16
|
-
def self.included(base)
|
18
|
+
def self.included(base)#:nodoc:
|
17
19
|
base.class_eval do
|
18
20
|
attr_accessor :rule_engine
|
19
21
|
attr_accessor :disable_rules
|
20
22
|
alias _add add
|
21
23
|
|
22
|
-
#
|
24
|
+
# Adding a triple to the database will trigger rule processing and it
|
25
|
+
# will continue to process rules until none of the rules will add new
|
26
|
+
# information to the database.
|
23
27
|
def add(*a)
|
24
28
|
r = _add(*a)
|
25
29
|
unless @disable_rules || @rule_engine.nil?
|