activerdf_rules 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ 0.0.1 - Initial version of the project. Pretty much everything is functional,
2
+ but not necessarily perfect.
@@ -1,13 +1,20 @@
1
+ CHANGELOG.txt
1
2
  History.txt
2
3
  Manifest.txt
3
4
  README.txt
4
5
  Rakefile
5
- setup.rb
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/test_helper.rb
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 defined using a DSL, and stored in memory. It comes
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 RDF Schema and OWL reasoning through the built-in
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. Also, I'm
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
@@ -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 'DSLRuleBase'
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 a ruby symbols and they can be used within both the
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 = @conclusion.collect {|t|
158
- instantiate(t, row, lookup)
159
- }
160
- old = ConnectionPool.write_adapter.size
161
- $activerdflog.debug("concluding #{triple.inspect}")
162
- ConnectionPool.write_adapter.add(*triple)
163
- # TODO HACK This should be changed
164
- # What I should be able to do is something like below, but the below is
165
- # broken for redland, and the above condition query is broken in
166
- # rdflite (because of ambiguous column names). For now this works.
167
- # Query.new.ask.where(*triple).execute
168
- if old < ConnectionPool.write_adapter.size
169
- change = true
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 RuleBase to add new data to an ActiveRDF data store
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
- attr_accessor :rule_base
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
- @rule_base = values[:rule_base]
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.rule_base = rb
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
- @rule_base.rules.each do |r|
36
- change |= r.process
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
- RDFSRuleBase = RuleBase.new 'RDFSRuleBase' do
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 where taken from
51
- # http://www-ksl.stanford.edu/software/jtp/doc/owl-reasoning.html
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
- # Type inheritance through rdfs:subclassOf
54
- rule do
55
- condition :Morris, type, :Cat
56
- condition :Cat, subClassOf, :Mammal
55
+ rule "rdf1" do
56
+ condition :s, :p, :o
57
57
 
58
- conclusion :Morris, type, :Mammal
58
+ conclusion :p, type, property
59
59
  end
60
60
 
61
- # Reflexivity of rdfs:subPropertyOf and rdfs:subclassOf
62
- rule do
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 :p, type, rdfclass
100
+ rule 'rdfs7' do
101
+ condition :parent, subPropertyOf, :ancestor
102
+ condition :Paul, :parent, :Lester
70
103
 
71
- conclusion :p, subClassOf, :p
104
+ conclusion :Paul, :ancestor, :Lester
72
105
  end
73
106
 
74
- # Type inference through rdfs:range and rdfs:domain constraints
75
- rule do
76
- condition :teaches, domain, :Teacher
77
- condition :Bob, :teaches, :Scooter
107
+ rule 'rdfs8' do
108
+ condition :c, type, rdfclass
78
109
 
79
- conclusion :Bob, type, :Teacher
110
+ conclusion :c, subClassOf, resource
80
111
  end
81
112
 
82
- rule do
83
- condition :teaches, range, :Student
84
- condition :Bob, :teaches, :Scooter
113
+ rule 'rdfs9' do
114
+ condition :Morris, type, :Cat
115
+ condition :Cat, subClassOf, :Mammal
85
116
 
86
- conclusion :Scooter, type, :Student
117
+ conclusion :Morris, type, :Mammal
87
118
  end
88
119
 
89
- # Transitivity of rdfs:subClassOf and rdfs:subPropertyOf
90
- rule do
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 :parent, subPropertyOf, :ancestor
99
- condition :ancestor, subPropertyOf, :relative
133
+ rule 'rdfs12' do
134
+ condition :p, type, container
100
135
 
101
- conclusion :parent, subPropertyOf, :relative
136
+ conclusion :p, subClassOf, member
102
137
  end
103
- end.freeze
104
- RDFSRuleBase.rules.freeze
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
- OWLRuleBase = RuleBase.new 'OWLRuleBase' do
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
- end.freeze
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 two included RuleBases one for RDF Schema, and one for OWL.
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
- #TODO
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?
@@ -2,7 +2,7 @@ module ActiveRDFRules #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 1
5
+ TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end