wongi-engine 0.0.14 → 0.0.16
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.
- checksums.yaml +4 -4
- data/.travis.yml +8 -0
- data/README.md +3 -11
- data/lib/wongi-engine/beta/beta_memory.rb +3 -4
- data/lib/wongi-engine/beta/beta_node.rb +5 -0
- data/lib/wongi-engine/beta/neg_node.rb +92 -94
- data/lib/wongi-engine/beta/production_node.rb +1 -2
- data/lib/wongi-engine/dsl/actions/statement_generator.rb +55 -55
- data/lib/wongi-engine/network.rb +442 -443
- data/lib/wongi-engine/token.rb +138 -133
- data/lib/wongi-engine/version.rb +1 -1
- data/lib/wongi-engine/wme.rb +138 -140
- data/spec/bug_specs/issue_4_spec.rb +9 -9
- data/spec/dataset_spec.rb +3 -3
- data/spec/filter_specs/assert_test_spec.rb +6 -6
- data/spec/filter_specs/less_test_spec.rb +1 -1
- data/spec/high_level_spec.rb +34 -34
- data/spec/rule_specs/any_rule_spec.rb +4 -4
- data/spec/rule_specs/assign_spec.rb +3 -4
- data/spec/rule_specs/maybe_rule_spec.rb +12 -12
- data/spec/rule_specs/ncc_spec.rb +6 -6
- data/spec/rule_specs/negative_rule_spec.rb +70 -70
- data/spec/ruleset_spec.rb +8 -8
- data/spec/simple_action_spec.rb +3 -3
- data/spec/spec_helper.rb +2 -0
- data/spec/wme_spec.rb +21 -21
- data/wongi-engine.gemspec +6 -2
- metadata +53 -9
data/lib/wongi-engine/network.rb
CHANGED
@@ -1,443 +1,442 @@
|
|
1
|
-
require 'wongi-engine/network/collectable'
|
2
|
-
require 'wongi-engine/network/debug'
|
3
|
-
|
4
|
-
module Wongi::Engine
|
5
|
-
class Network
|
6
|
-
|
7
|
-
attr_reader :alpha_top, :beta_top
|
8
|
-
attr_reader :queries, :results
|
9
|
-
attr_reader :productions
|
10
|
-
|
11
|
-
include NetworkParts::Collectable
|
12
|
-
|
13
|
-
protected
|
14
|
-
attr_accessor :alpha_hash
|
15
|
-
attr_writer :alpha_top, :beta_top
|
16
|
-
attr_writer :queries, :results
|
17
|
-
|
18
|
-
public
|
19
|
-
|
20
|
-
def debug!
|
21
|
-
extend NetworkParts::Debug
|
22
|
-
end
|
23
|
-
|
24
|
-
def rdf!
|
25
|
-
if ! defined? Wongi::RDF::DocumentSupport
|
26
|
-
begin
|
27
|
-
require 'wongi-rdf'
|
28
|
-
rescue LoadError => e
|
29
|
-
raise "'wongi-rdf' is required for RDF support"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
extend Wongi::RDF::DocumentSupport
|
34
|
-
class << self
|
35
|
-
def statements
|
36
|
-
alpha_top.wmes
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
@namespaces = { }
|
41
|
-
@blank_counter = 1
|
42
|
-
@ns_counter = 0
|
43
|
-
@used_blanks = { }
|
44
|
-
end
|
45
|
-
|
46
|
-
def initialize
|
47
|
-
@timeline = []
|
48
|
-
self.alpha_top = AlphaMemory.new( Template.new( :_, :_, :_ ), self )
|
49
|
-
self.alpha_hash = { Template.hash_for( :_, :_, :_ ) => self.alpha_top }
|
50
|
-
self.beta_top = BetaMemory.new(nil)
|
51
|
-
self.beta_top.rete = self
|
52
|
-
self.beta_top.seed
|
53
|
-
self.queries = {}
|
54
|
-
self.results = {}
|
55
|
-
@cache = {}
|
56
|
-
@revns = {}
|
57
|
-
@contexts = {}
|
58
|
-
|
59
|
-
@productions = { }
|
60
|
-
|
61
|
-
@collectors = {}
|
62
|
-
@collectors[:error] = []
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
def dump
|
67
|
-
beta_top.dump
|
68
|
-
end
|
69
|
-
|
70
|
-
def alphas
|
71
|
-
alpha_hash.values
|
72
|
-
end
|
73
|
-
|
74
|
-
def import thing
|
75
|
-
case thing
|
76
|
-
when String, Numeric, TrueClass, FalseClass, NilClass, Wongi::RDF::Node
|
77
|
-
thing
|
78
|
-
when Symbol
|
79
|
-
thing
|
80
|
-
else
|
81
|
-
thing
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def assert wme
|
86
|
-
|
87
|
-
unless wme.rete == self
|
88
|
-
wme = wme.import_into self
|
89
|
-
end
|
90
|
-
|
91
|
-
if @current_context
|
92
|
-
@current_context.asserted_wmes << wme
|
93
|
-
wme.context = @current_context
|
94
|
-
end
|
95
|
-
|
96
|
-
return if @cache.has_key?(wme)
|
97
|
-
|
98
|
-
# puts "ASSERTING #{wme}"
|
99
|
-
@cache[wme] = wme
|
100
|
-
|
101
|
-
s = wme.subject
|
102
|
-
p = wme.predicate
|
103
|
-
o = wme.object
|
104
|
-
|
105
|
-
alpha_activate(lookup( s, p, o), wme)
|
106
|
-
alpha_activate(lookup( s, p, :_), wme)
|
107
|
-
alpha_activate(lookup( s, :_, o), wme)
|
108
|
-
alpha_activate(lookup(:_, p, o), wme)
|
109
|
-
alpha_activate(lookup( s, :_, :_), wme)
|
110
|
-
alpha_activate(lookup(:_, p, :_), wme)
|
111
|
-
alpha_activate(lookup(:_, :_, o), wme)
|
112
|
-
alpha_activate(lookup(:_, :_, :_), wme)
|
113
|
-
|
114
|
-
wme
|
115
|
-
end
|
116
|
-
|
117
|
-
def wmes
|
118
|
-
alpha_top.wmes
|
119
|
-
end
|
120
|
-
alias_method :statements, :wmes
|
121
|
-
alias_method :facts, :wmes
|
122
|
-
|
123
|
-
def in_snapshot?
|
124
|
-
@in_snapshot
|
125
|
-
end
|
126
|
-
|
127
|
-
def snapshot!
|
128
|
-
@timeline.each_with_index do |slice, index|
|
129
|
-
source = if index == @timeline.size - 1
|
130
|
-
alpha_hash
|
131
|
-
else
|
132
|
-
@timeline[index+1]
|
133
|
-
end
|
134
|
-
# puts "source = #{source}"
|
135
|
-
wmes = {}
|
136
|
-
slice.each { |key, alpha| wmes[key] = alpha.wmes }
|
137
|
-
slice.each do |key, alpha|
|
138
|
-
in_snapshot {
|
139
|
-
wmes[key].dup.each { |wme| wme.destroy }
|
140
|
-
}
|
141
|
-
alpha.snapshot! source[key]
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def rule name = nil, &block
|
147
|
-
r = ProductionRule.new( name || generate_rule_name )
|
148
|
-
r.instance_eval &block
|
149
|
-
self << r
|
150
|
-
end
|
151
|
-
|
152
|
-
def query name, &block
|
153
|
-
q = Query.new name
|
154
|
-
q.instance_eval &block
|
155
|
-
self << q
|
156
|
-
end
|
157
|
-
|
158
|
-
def << something
|
159
|
-
case something
|
160
|
-
when Array
|
161
|
-
if something.length == 3
|
162
|
-
assert WME.new( *something )
|
163
|
-
else
|
164
|
-
raise "Arrays must have 3 elements"
|
165
|
-
end
|
166
|
-
when ProductionRule
|
167
|
-
derived = something.import_into self
|
168
|
-
production = add_production derived.conditions, derived.actions
|
169
|
-
if something.name
|
170
|
-
productions[ something.name ] = production
|
171
|
-
end
|
172
|
-
when Query
|
173
|
-
derived = something.import_into self
|
174
|
-
prepare_query derived.name, derived.conditions, derived.parameters, derived.actions
|
175
|
-
when Ruleset
|
176
|
-
something.install self
|
177
|
-
when WME
|
178
|
-
assert something
|
179
|
-
when Wongi::RDF::Statement
|
180
|
-
assert WME.new( something.subject, something.predicate, something.object, self )
|
181
|
-
#when Wongi::RDF::Document
|
182
|
-
# something.statements.each do |st|
|
183
|
-
# assert WME.new( st.subject, st.predicate, st.object, self )
|
184
|
-
# end
|
185
|
-
when Network
|
186
|
-
something.each do |st|
|
187
|
-
assert st.import_into( self )
|
188
|
-
end
|
189
|
-
else
|
190
|
-
raise "I don't know how to accept a #{something.class}"
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
def retract wme, is_real = false
|
195
|
-
|
196
|
-
if wme.is_a? Array
|
197
|
-
return retract( WME.new(*wme), is_real )
|
198
|
-
end
|
199
|
-
|
200
|
-
if ! is_real
|
201
|
-
if @current_context
|
202
|
-
@current_context.retracted_wmes << wme
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
real = if is_real
|
207
|
-
wme
|
208
|
-
else
|
209
|
-
#find(wme.subject, wme.predicate, wme.object)
|
210
|
-
@cache[wme]
|
211
|
-
end
|
212
|
-
|
213
|
-
return false if real.nil?
|
214
|
-
@cache.delete(real)
|
215
|
-
raise "Cannot retract inferred statements" unless real.manual?
|
216
|
-
|
217
|
-
real.destroy
|
218
|
-
|
219
|
-
true
|
220
|
-
|
221
|
-
end
|
222
|
-
|
223
|
-
def compile_alpha condition
|
224
|
-
template = Template.new :_, :_, :_
|
225
|
-
time = condition.time
|
226
|
-
|
227
|
-
template.subject = condition.subject unless Template.variable?( condition.subject )
|
228
|
-
template.predicate = condition.predicate unless Template.variable?( condition.predicate )
|
229
|
-
template.object = condition.object unless Template.variable?( condition.object )
|
230
|
-
|
231
|
-
hash = template.hash
|
232
|
-
# puts "COMPILED CONDITION #{condition} WITH KEY #{key}"
|
233
|
-
if time == 0
|
234
|
-
return self.alpha_hash[ hash ] if self.alpha_hash.has_key?( hash )
|
235
|
-
else
|
236
|
-
return @timeline[time+1][ hash ] if @timeline[time+1] && @timeline[time+1].has_key?( hash )
|
237
|
-
end
|
238
|
-
|
239
|
-
alpha = AlphaMemory.new( template, self )
|
240
|
-
|
241
|
-
if time == 0
|
242
|
-
self.alpha_hash[ hash ] = alpha
|
243
|
-
initial_fill alpha
|
244
|
-
else
|
245
|
-
if @timeline[time+1].nil?
|
246
|
-
# => ensure lineage from 0 to time
|
247
|
-
compile_alpha condition.class.new(condition.subject, condition.predicate, condition.object, time: time + 1)
|
248
|
-
@timeline.unshift Hash.new
|
249
|
-
end
|
250
|
-
@timeline[time+1][ hash ] = alpha
|
251
|
-
end
|
252
|
-
alpha
|
253
|
-
end
|
254
|
-
|
255
|
-
def cache s, p, o
|
256
|
-
compile_alpha Template.new(s, p, o).import_into( self )
|
257
|
-
end
|
258
|
-
|
259
|
-
def initial_fill alpha
|
260
|
-
tpl = alpha.template
|
261
|
-
source = more_generic_alpha(tpl)
|
262
|
-
# puts "more efficient by #{alpha_top.wmes.size - source.wmes.size}" unless source ==
|
263
|
-
# alpha_top
|
264
|
-
source.wmes.each do |wme|
|
265
|
-
alpha.activate wme if wme =~ tpl
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
def add_production conditions, actions = []
|
270
|
-
real_add_production self.beta_top, conditions, [], actions, false
|
271
|
-
end
|
272
|
-
|
273
|
-
def remove_production pnode
|
274
|
-
delete_node_with_ancestors pnode
|
275
|
-
end
|
276
|
-
|
277
|
-
def prepare_query name, conditions, parameters, actions = []
|
278
|
-
query = self.queries[ name ] = BetaMemory.new( nil )
|
279
|
-
query.rete = self
|
280
|
-
transformed = {}
|
281
|
-
parameters.each { |param| transformed[param] = nil }
|
282
|
-
query.seed transformed
|
283
|
-
self.results[ name ] = real_add_production query, conditions, parameters, actions, true
|
284
|
-
end
|
285
|
-
|
286
|
-
def execute name, valuations
|
287
|
-
beta = self.queries[name]
|
288
|
-
raise "Undefined query #{name}; known queries are #{queries.keys}" unless beta
|
289
|
-
beta.subst valuations
|
290
|
-
end
|
291
|
-
|
292
|
-
def inspect
|
293
|
-
"<Rete>"
|
294
|
-
end
|
295
|
-
|
296
|
-
def context= name
|
297
|
-
if name && !@contexts.has_key?(name)
|
298
|
-
@current_context = (@contexts[name] ||= ModelContext.new name)
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
def retract_context name
|
303
|
-
return unless @contexts.has_key?(name)
|
304
|
-
|
305
|
-
if @current_context && @current_context.name == name
|
306
|
-
@current_context = nil
|
307
|
-
end
|
308
|
-
ctx = @contexts[name]
|
309
|
-
ctx.asserted_wmes.select { |wme| wme.generating_tokens.empty? }.each { |wme| retract(wme, true) }
|
310
|
-
ctx.retracted_wmes.each { |wme| assert(wme) }
|
311
|
-
@contexts.delete name
|
312
|
-
end
|
313
|
-
|
314
|
-
def exists? wme
|
315
|
-
@cache[ wme ]
|
316
|
-
end
|
317
|
-
|
318
|
-
def each *args
|
319
|
-
return unless block_given?
|
320
|
-
unless args.length == 0 || args.length == 3
|
321
|
-
raise "Document#each expects a pattern or nothing at all"
|
322
|
-
end
|
323
|
-
s, p, o = if args.empty?
|
324
|
-
[:_, :_, :_]
|
325
|
-
else
|
326
|
-
args
|
327
|
-
end
|
328
|
-
no_check = s == :_ && p == :_ && o == :_
|
329
|
-
template = Template.new(s, p, o).import_into self
|
330
|
-
alpha_top.wmes.each do |wme|
|
331
|
-
yield wme if (no_check || wme =~ template)
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
def select s, p, o
|
336
|
-
template = Template.new(s, p, o).import_into self
|
337
|
-
matching = alpha_top.wmes.select { |wme| wme =~ template }
|
338
|
-
if block_given?
|
339
|
-
matching.each { |st| yield st.subject, st.predicate, st.object }
|
340
|
-
end
|
341
|
-
matching
|
342
|
-
end
|
343
|
-
|
344
|
-
def find s, p, o
|
345
|
-
template = Template.new(s, p, o).import_into self
|
346
|
-
source = best_alpha(template)
|
347
|
-
# puts "looking for #{template} among #{source.wmes.size} triples of #{source.template}"
|
348
|
-
source.wmes.detect { |wme| wme =~ template }
|
349
|
-
end
|
350
|
-
|
351
|
-
protected
|
352
|
-
|
353
|
-
def in_snapshot
|
354
|
-
@in_snapshot = true
|
355
|
-
yield
|
356
|
-
ensure
|
357
|
-
@in_snapshot = false
|
358
|
-
end
|
359
|
-
|
360
|
-
def generate_rule_name
|
361
|
-
"rule_#{productions.length}"
|
362
|
-
end
|
363
|
-
|
364
|
-
def lookup s, p, o
|
365
|
-
key = Template.hash_for(s, p, o)
|
366
|
-
# puts "Lookup for #{key}"
|
367
|
-
self.alpha_hash[ key ]
|
368
|
-
end
|
369
|
-
|
370
|
-
def alpha_activate alpha, wme
|
371
|
-
alpha.activate(wme) if alpha
|
372
|
-
end
|
373
|
-
|
374
|
-
def more_generic_alpha template
|
375
|
-
return alpha_top # OPTIMISE => temporary; may use later or not use at all
|
376
|
-
return alpha_top if template.root?
|
377
|
-
more_generic_templates(template).reduce alpha_top do |best, template|
|
378
|
-
alpha = alpha_hash[template.hash]
|
379
|
-
if alpha && alpha.wmes.size < best.wmes.size
|
380
|
-
alpha
|
381
|
-
else
|
382
|
-
best
|
383
|
-
end
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
def more_generic_templates template
|
388
|
-
set = []
|
389
|
-
set << template.with_subject( :_ ) unless template.subject == :_
|
390
|
-
set << template.with_predicate( :_ ) unless template.predicate == :_
|
391
|
-
set << template.with_object( :_ ) unless template.object == :_
|
392
|
-
set.select { |item| not item.root? }
|
393
|
-
end
|
394
|
-
|
395
|
-
def best_alpha template
|
396
|
-
candidates = alpha_hash.values.select do |alpha|
|
397
|
-
template =~ alpha.template
|
398
|
-
end
|
399
|
-
result = candidates.inject do |best, alpha|
|
400
|
-
if best.nil?
|
401
|
-
alpha
|
402
|
-
elsif alpha.wmes.to_a.length < best.wmes.to_a.length
|
403
|
-
alpha
|
404
|
-
else
|
405
|
-
best
|
406
|
-
end
|
407
|
-
end
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
production
|
416
|
-
production
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
node.parent.children.
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
end
|
1
|
+
require 'wongi-engine/network/collectable'
|
2
|
+
require 'wongi-engine/network/debug'
|
3
|
+
|
4
|
+
module Wongi::Engine
|
5
|
+
class Network
|
6
|
+
|
7
|
+
attr_reader :alpha_top, :beta_top
|
8
|
+
attr_reader :queries, :results
|
9
|
+
attr_reader :productions
|
10
|
+
|
11
|
+
include NetworkParts::Collectable
|
12
|
+
|
13
|
+
protected
|
14
|
+
attr_accessor :alpha_hash
|
15
|
+
attr_writer :alpha_top, :beta_top
|
16
|
+
attr_writer :queries, :results
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
def debug!
|
21
|
+
extend NetworkParts::Debug
|
22
|
+
end
|
23
|
+
|
24
|
+
def rdf!
|
25
|
+
if ! defined? Wongi::RDF::DocumentSupport
|
26
|
+
begin
|
27
|
+
require 'wongi-rdf'
|
28
|
+
rescue LoadError => e
|
29
|
+
raise "'wongi-rdf' is required for RDF support"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
extend Wongi::RDF::DocumentSupport
|
34
|
+
class << self
|
35
|
+
def statements
|
36
|
+
alpha_top.wmes
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@namespaces = { }
|
41
|
+
@blank_counter = 1
|
42
|
+
@ns_counter = 0
|
43
|
+
@used_blanks = { }
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
@timeline = []
|
48
|
+
self.alpha_top = AlphaMemory.new( Template.new( :_, :_, :_ ), self )
|
49
|
+
self.alpha_hash = { Template.hash_for( :_, :_, :_ ) => self.alpha_top }
|
50
|
+
self.beta_top = BetaMemory.new(nil)
|
51
|
+
self.beta_top.rete = self
|
52
|
+
self.beta_top.seed
|
53
|
+
self.queries = {}
|
54
|
+
self.results = {}
|
55
|
+
@cache = {}
|
56
|
+
@revns = {}
|
57
|
+
@contexts = {}
|
58
|
+
|
59
|
+
@productions = { }
|
60
|
+
|
61
|
+
@collectors = {}
|
62
|
+
@collectors[:error] = []
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
def dump
|
67
|
+
beta_top.dump
|
68
|
+
end
|
69
|
+
|
70
|
+
def alphas
|
71
|
+
alpha_hash.values
|
72
|
+
end
|
73
|
+
|
74
|
+
def import thing
|
75
|
+
case thing
|
76
|
+
when String, Numeric, TrueClass, FalseClass, NilClass, Wongi::RDF::Node
|
77
|
+
thing
|
78
|
+
when Symbol
|
79
|
+
thing
|
80
|
+
else
|
81
|
+
thing
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def assert wme
|
86
|
+
|
87
|
+
unless wme.rete == self
|
88
|
+
wme = wme.import_into self
|
89
|
+
end
|
90
|
+
|
91
|
+
if @current_context
|
92
|
+
@current_context.asserted_wmes << wme
|
93
|
+
wme.context = @current_context
|
94
|
+
end
|
95
|
+
|
96
|
+
return if @cache.has_key?(wme)
|
97
|
+
|
98
|
+
# puts "ASSERTING #{wme}"
|
99
|
+
@cache[wme] = wme
|
100
|
+
|
101
|
+
s = wme.subject
|
102
|
+
p = wme.predicate
|
103
|
+
o = wme.object
|
104
|
+
|
105
|
+
alpha_activate(lookup( s, p, o), wme)
|
106
|
+
alpha_activate(lookup( s, p, :_), wme)
|
107
|
+
alpha_activate(lookup( s, :_, o), wme)
|
108
|
+
alpha_activate(lookup(:_, p, o), wme)
|
109
|
+
alpha_activate(lookup( s, :_, :_), wme)
|
110
|
+
alpha_activate(lookup(:_, p, :_), wme)
|
111
|
+
alpha_activate(lookup(:_, :_, o), wme)
|
112
|
+
alpha_activate(lookup(:_, :_, :_), wme)
|
113
|
+
|
114
|
+
wme
|
115
|
+
end
|
116
|
+
|
117
|
+
def wmes
|
118
|
+
alpha_top.wmes
|
119
|
+
end
|
120
|
+
alias_method :statements, :wmes
|
121
|
+
alias_method :facts, :wmes
|
122
|
+
|
123
|
+
def in_snapshot?
|
124
|
+
@in_snapshot
|
125
|
+
end
|
126
|
+
|
127
|
+
def snapshot!
|
128
|
+
@timeline.each_with_index do |slice, index|
|
129
|
+
source = if index == @timeline.size - 1
|
130
|
+
alpha_hash
|
131
|
+
else
|
132
|
+
@timeline[index+1]
|
133
|
+
end
|
134
|
+
# puts "source = #{source}"
|
135
|
+
wmes = {}
|
136
|
+
slice.each { |key, alpha| wmes[key] = alpha.wmes }
|
137
|
+
slice.each do |key, alpha|
|
138
|
+
in_snapshot {
|
139
|
+
wmes[key].dup.each { |wme| wme.destroy }
|
140
|
+
}
|
141
|
+
alpha.snapshot! source[key]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def rule name = nil, &block
|
147
|
+
r = ProductionRule.new( name || generate_rule_name )
|
148
|
+
r.instance_eval &block
|
149
|
+
self << r
|
150
|
+
end
|
151
|
+
|
152
|
+
def query name, &block
|
153
|
+
q = Query.new name
|
154
|
+
q.instance_eval &block
|
155
|
+
self << q
|
156
|
+
end
|
157
|
+
|
158
|
+
def << something
|
159
|
+
case something
|
160
|
+
when Array
|
161
|
+
if something.length == 3
|
162
|
+
assert WME.new( *something )
|
163
|
+
else
|
164
|
+
raise "Arrays must have 3 elements"
|
165
|
+
end
|
166
|
+
when ProductionRule
|
167
|
+
derived = something.import_into self
|
168
|
+
production = add_production derived.conditions, derived.actions
|
169
|
+
if something.name
|
170
|
+
productions[ something.name ] = production
|
171
|
+
end
|
172
|
+
when Query
|
173
|
+
derived = something.import_into self
|
174
|
+
prepare_query derived.name, derived.conditions, derived.parameters, derived.actions
|
175
|
+
when Ruleset
|
176
|
+
something.install self
|
177
|
+
when WME
|
178
|
+
assert something
|
179
|
+
when Wongi::RDF::Statement
|
180
|
+
assert WME.new( something.subject, something.predicate, something.object, self )
|
181
|
+
#when Wongi::RDF::Document
|
182
|
+
# something.statements.each do |st|
|
183
|
+
# assert WME.new( st.subject, st.predicate, st.object, self )
|
184
|
+
# end
|
185
|
+
when Network
|
186
|
+
something.each do |st|
|
187
|
+
assert st.import_into( self )
|
188
|
+
end
|
189
|
+
else
|
190
|
+
raise "I don't know how to accept a #{something.class}"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def retract wme, is_real = false
|
195
|
+
|
196
|
+
if wme.is_a? Array
|
197
|
+
return retract( WME.new(*wme), is_real )
|
198
|
+
end
|
199
|
+
|
200
|
+
if ! is_real
|
201
|
+
if @current_context
|
202
|
+
@current_context.retracted_wmes << wme
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
real = if is_real
|
207
|
+
wme
|
208
|
+
else
|
209
|
+
#find(wme.subject, wme.predicate, wme.object)
|
210
|
+
@cache[wme]
|
211
|
+
end
|
212
|
+
|
213
|
+
return false if real.nil?
|
214
|
+
@cache.delete(real)
|
215
|
+
raise "Cannot retract inferred statements" unless real.manual?
|
216
|
+
|
217
|
+
real.destroy
|
218
|
+
|
219
|
+
true
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
def compile_alpha condition
|
224
|
+
template = Template.new :_, :_, :_
|
225
|
+
time = condition.time
|
226
|
+
|
227
|
+
template.subject = condition.subject unless Template.variable?( condition.subject )
|
228
|
+
template.predicate = condition.predicate unless Template.variable?( condition.predicate )
|
229
|
+
template.object = condition.object unless Template.variable?( condition.object )
|
230
|
+
|
231
|
+
hash = template.hash
|
232
|
+
# puts "COMPILED CONDITION #{condition} WITH KEY #{key}"
|
233
|
+
if time == 0
|
234
|
+
return self.alpha_hash[ hash ] if self.alpha_hash.has_key?( hash )
|
235
|
+
else
|
236
|
+
return @timeline[time+1][ hash ] if @timeline[time+1] && @timeline[time+1].has_key?( hash )
|
237
|
+
end
|
238
|
+
|
239
|
+
alpha = AlphaMemory.new( template, self )
|
240
|
+
|
241
|
+
if time == 0
|
242
|
+
self.alpha_hash[ hash ] = alpha
|
243
|
+
initial_fill alpha
|
244
|
+
else
|
245
|
+
if @timeline[time+1].nil?
|
246
|
+
# => ensure lineage from 0 to time
|
247
|
+
compile_alpha condition.class.new(condition.subject, condition.predicate, condition.object, time: time + 1)
|
248
|
+
@timeline.unshift Hash.new
|
249
|
+
end
|
250
|
+
@timeline[time+1][ hash ] = alpha
|
251
|
+
end
|
252
|
+
alpha
|
253
|
+
end
|
254
|
+
|
255
|
+
def cache s, p, o
|
256
|
+
compile_alpha Template.new(s, p, o).import_into( self )
|
257
|
+
end
|
258
|
+
|
259
|
+
def initial_fill alpha
|
260
|
+
tpl = alpha.template
|
261
|
+
source = more_generic_alpha(tpl)
|
262
|
+
# puts "more efficient by #{alpha_top.wmes.size - source.wmes.size}" unless source ==
|
263
|
+
# alpha_top
|
264
|
+
source.wmes.each do |wme|
|
265
|
+
alpha.activate wme if wme =~ tpl
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def add_production conditions, actions = []
|
270
|
+
real_add_production self.beta_top, conditions, [], actions, false
|
271
|
+
end
|
272
|
+
|
273
|
+
def remove_production pnode
|
274
|
+
delete_node_with_ancestors pnode
|
275
|
+
end
|
276
|
+
|
277
|
+
def prepare_query name, conditions, parameters, actions = []
|
278
|
+
query = self.queries[ name ] = BetaMemory.new( nil )
|
279
|
+
query.rete = self
|
280
|
+
transformed = {}
|
281
|
+
parameters.each { |param| transformed[param] = nil }
|
282
|
+
query.seed transformed
|
283
|
+
self.results[ name ] = real_add_production query, conditions, parameters, actions, true
|
284
|
+
end
|
285
|
+
|
286
|
+
def execute name, valuations
|
287
|
+
beta = self.queries[name]
|
288
|
+
raise "Undefined query #{name}; known queries are #{queries.keys}" unless beta
|
289
|
+
beta.subst valuations
|
290
|
+
end
|
291
|
+
|
292
|
+
def inspect
|
293
|
+
"<Rete>"
|
294
|
+
end
|
295
|
+
|
296
|
+
def context= name
|
297
|
+
if name && !@contexts.has_key?(name)
|
298
|
+
@current_context = (@contexts[name] ||= ModelContext.new name)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def retract_context name
|
303
|
+
return unless @contexts.has_key?(name)
|
304
|
+
|
305
|
+
if @current_context && @current_context.name == name
|
306
|
+
@current_context = nil
|
307
|
+
end
|
308
|
+
ctx = @contexts[name]
|
309
|
+
ctx.asserted_wmes.select { |wme| wme.generating_tokens.empty? }.each { |wme| retract(wme, true) }
|
310
|
+
ctx.retracted_wmes.each { |wme| assert(wme) }
|
311
|
+
@contexts.delete name
|
312
|
+
end
|
313
|
+
|
314
|
+
def exists? wme
|
315
|
+
@cache[ wme ]
|
316
|
+
end
|
317
|
+
|
318
|
+
def each *args
|
319
|
+
return unless block_given?
|
320
|
+
unless args.length == 0 || args.length == 3
|
321
|
+
raise "Document#each expects a pattern or nothing at all"
|
322
|
+
end
|
323
|
+
s, p, o = if args.empty?
|
324
|
+
[:_, :_, :_]
|
325
|
+
else
|
326
|
+
args
|
327
|
+
end
|
328
|
+
no_check = s == :_ && p == :_ && o == :_
|
329
|
+
template = Template.new(s, p, o).import_into self
|
330
|
+
alpha_top.wmes.each do |wme|
|
331
|
+
yield wme if (no_check || wme =~ template)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def select s, p, o
|
336
|
+
template = Template.new(s, p, o).import_into self
|
337
|
+
matching = alpha_top.wmes.select { |wme| wme =~ template }
|
338
|
+
if block_given?
|
339
|
+
matching.each { |st| yield st.subject, st.predicate, st.object }
|
340
|
+
end
|
341
|
+
matching
|
342
|
+
end
|
343
|
+
|
344
|
+
def find s, p, o
|
345
|
+
template = Template.new(s, p, o).import_into self
|
346
|
+
source = best_alpha(template)
|
347
|
+
# puts "looking for #{template} among #{source.wmes.size} triples of #{source.template}"
|
348
|
+
source.wmes.detect { |wme| wme =~ template }
|
349
|
+
end
|
350
|
+
|
351
|
+
protected
|
352
|
+
|
353
|
+
def in_snapshot
|
354
|
+
@in_snapshot = true
|
355
|
+
yield
|
356
|
+
ensure
|
357
|
+
@in_snapshot = false
|
358
|
+
end
|
359
|
+
|
360
|
+
def generate_rule_name
|
361
|
+
"rule_#{productions.length}"
|
362
|
+
end
|
363
|
+
|
364
|
+
def lookup s, p, o
|
365
|
+
key = Template.hash_for(s, p, o)
|
366
|
+
# puts "Lookup for #{key}"
|
367
|
+
self.alpha_hash[ key ]
|
368
|
+
end
|
369
|
+
|
370
|
+
def alpha_activate alpha, wme
|
371
|
+
alpha.activate(wme) if alpha
|
372
|
+
end
|
373
|
+
|
374
|
+
def more_generic_alpha template
|
375
|
+
return alpha_top # OPTIMISE => temporary; may use later or not use at all
|
376
|
+
return alpha_top if template.root?
|
377
|
+
more_generic_templates(template).reduce alpha_top do |best, template|
|
378
|
+
alpha = alpha_hash[template.hash]
|
379
|
+
if alpha && alpha.wmes.size < best.wmes.size
|
380
|
+
alpha
|
381
|
+
else
|
382
|
+
best
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def more_generic_templates template
|
388
|
+
set = []
|
389
|
+
set << template.with_subject( :_ ) unless template.subject == :_
|
390
|
+
set << template.with_predicate( :_ ) unless template.predicate == :_
|
391
|
+
set << template.with_object( :_ ) unless template.object == :_
|
392
|
+
set.select { |item| not item.root? }
|
393
|
+
end
|
394
|
+
|
395
|
+
def best_alpha template
|
396
|
+
candidates = alpha_hash.values.select do |alpha|
|
397
|
+
template =~ alpha.template
|
398
|
+
end
|
399
|
+
result = candidates.inject do |best, alpha|
|
400
|
+
if best.nil?
|
401
|
+
alpha
|
402
|
+
elsif alpha.wmes.to_a.length < best.wmes.to_a.length
|
403
|
+
alpha
|
404
|
+
else
|
405
|
+
best
|
406
|
+
end
|
407
|
+
end
|
408
|
+
result
|
409
|
+
end
|
410
|
+
|
411
|
+
def real_add_production root, conditions, parameters, actions, alpha_deaf
|
412
|
+
beta = root.network conditions, [], parameters, alpha_deaf
|
413
|
+
|
414
|
+
production = ProductionNode.new( beta, actions )
|
415
|
+
production.refresh
|
416
|
+
production
|
417
|
+
end
|
418
|
+
|
419
|
+
def delete_node_with_ancestors node
|
420
|
+
|
421
|
+
if node.kind_of?( NccNode )
|
422
|
+
delete_node_with_ancestors node.partner
|
423
|
+
end
|
424
|
+
|
425
|
+
if [BetaMemory, NegNode, NccNode, NccPartner].any? { | klass| node.kind_of? klass }
|
426
|
+
while node.tokens.first
|
427
|
+
node.tokens.first.delete
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
if node.parent
|
432
|
+
node.parent.children.delete node
|
433
|
+
if node.parent.children.empty?
|
434
|
+
delete_node_with_ancestors(node.parent)
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
end
|
439
|
+
|
440
|
+
end
|
441
|
+
|
442
|
+
end
|