mort666-wongi-engine 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.hgignore +6 -0
  4. data/.hgtags +13 -0
  5. data/.ruby-gemset +1 -0
  6. data/.travis.yml +19 -0
  7. data/CHANGELOG.md +106 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +22 -0
  10. data/README.md +27 -0
  11. data/Rakefile +9 -0
  12. data/examples/ex01.rb +23 -0
  13. data/examples/ex02.rb +37 -0
  14. data/examples/graphviz.rb +16 -0
  15. data/examples/rdf.n3 +6 -0
  16. data/examples/rdf.rb +14 -0
  17. data/examples/timeline.rb +48 -0
  18. data/lib/wongi-engine.rb +36 -0
  19. data/lib/wongi-engine/alpha_memory.rb +60 -0
  20. data/lib/wongi-engine/beta.rb +11 -0
  21. data/lib/wongi-engine/beta/assignment_node.rb +40 -0
  22. data/lib/wongi-engine/beta/beta_memory.rb +49 -0
  23. data/lib/wongi-engine/beta/beta_node.rb +94 -0
  24. data/lib/wongi-engine/beta/filter_node.rb +48 -0
  25. data/lib/wongi-engine/beta/join_node.rb +140 -0
  26. data/lib/wongi-engine/beta/ncc_node.rb +67 -0
  27. data/lib/wongi-engine/beta/ncc_partner.rb +40 -0
  28. data/lib/wongi-engine/beta/neg_node.rb +115 -0
  29. data/lib/wongi-engine/beta/optional_node.rb +142 -0
  30. data/lib/wongi-engine/beta/or_node.rb +37 -0
  31. data/lib/wongi-engine/beta/production_node.rb +31 -0
  32. data/lib/wongi-engine/compiler.rb +115 -0
  33. data/lib/wongi-engine/core_ext.rb +63 -0
  34. data/lib/wongi-engine/data_overlay.rb +144 -0
  35. data/lib/wongi-engine/dsl.rb +132 -0
  36. data/lib/wongi-engine/dsl/action/base.rb +11 -0
  37. data/lib/wongi-engine/dsl/action/error_generator.rb +31 -0
  38. data/lib/wongi-engine/dsl/action/simple_action.rb +60 -0
  39. data/lib/wongi-engine/dsl/action/simple_collector.rb +52 -0
  40. data/lib/wongi-engine/dsl/action/statement_generator.rb +46 -0
  41. data/lib/wongi-engine/dsl/action/trace_action.rb +49 -0
  42. data/lib/wongi-engine/dsl/any_rule.rb +33 -0
  43. data/lib/wongi-engine/dsl/assuming.rb +31 -0
  44. data/lib/wongi-engine/dsl/builder.rb +44 -0
  45. data/lib/wongi-engine/dsl/clause/assign.rb +15 -0
  46. data/lib/wongi-engine/dsl/clause/fact.rb +71 -0
  47. data/lib/wongi-engine/dsl/clause/gen.rb +17 -0
  48. data/lib/wongi-engine/dsl/clause/generic.rb +38 -0
  49. data/lib/wongi-engine/dsl/generated.rb +43 -0
  50. data/lib/wongi-engine/dsl/ncc_subrule.rb +17 -0
  51. data/lib/wongi-engine/dsl/query.rb +24 -0
  52. data/lib/wongi-engine/dsl/rule.rb +84 -0
  53. data/lib/wongi-engine/enumerators.rb +21 -0
  54. data/lib/wongi-engine/error.rb +22 -0
  55. data/lib/wongi-engine/filter.rb +6 -0
  56. data/lib/wongi-engine/filter/asserting_test.rb +20 -0
  57. data/lib/wongi-engine/filter/equality_test.rb +36 -0
  58. data/lib/wongi-engine/filter/filter_test.rb +18 -0
  59. data/lib/wongi-engine/filter/greater_than_test.rb +36 -0
  60. data/lib/wongi-engine/filter/inequality_test.rb +36 -0
  61. data/lib/wongi-engine/filter/less_than_test.rb +36 -0
  62. data/lib/wongi-engine/graph.rb +73 -0
  63. data/lib/wongi-engine/network.rb +416 -0
  64. data/lib/wongi-engine/network/collectable.rb +42 -0
  65. data/lib/wongi-engine/network/debug.rb +85 -0
  66. data/lib/wongi-engine/ruleset.rb +74 -0
  67. data/lib/wongi-engine/template.rb +78 -0
  68. data/lib/wongi-engine/token.rb +114 -0
  69. data/lib/wongi-engine/version.rb +5 -0
  70. data/lib/wongi-engine/wme.rb +89 -0
  71. data/lib/wongi-engine/wme_match_data.rb +34 -0
  72. data/spec/beta_node_spec.rb +29 -0
  73. data/spec/bug_specs/issue_4_spec.rb +141 -0
  74. data/spec/dataset_spec.rb +27 -0
  75. data/spec/dsl_spec.rb +9 -0
  76. data/spec/filter_specs/assert_test_spec.rb +102 -0
  77. data/spec/filter_specs/less_test_spec.rb +41 -0
  78. data/spec/generation_spec.rb +116 -0
  79. data/spec/high_level_spec.rb +378 -0
  80. data/spec/network_spec.rb +182 -0
  81. data/spec/overlay_spec.rb +61 -0
  82. data/spec/rule_specs/any_rule_spec.rb +75 -0
  83. data/spec/rule_specs/assign_spec.rb +88 -0
  84. data/spec/rule_specs/assuming_spec.rb +66 -0
  85. data/spec/rule_specs/maybe_rule_spec.rb +101 -0
  86. data/spec/rule_specs/ncc_spec.rb +258 -0
  87. data/spec/rule_specs/negative_rule_spec.rb +105 -0
  88. data/spec/ruleset_spec.rb +54 -0
  89. data/spec/simple_action_spec.rb +40 -0
  90. data/spec/spec_helper.rb +3 -0
  91. data/spec/wme_spec.rb +83 -0
  92. data/wongi-engine.gemspec +40 -0
  93. metadata +212 -0
@@ -0,0 +1,378 @@
1
+ require 'spec_helper'
2
+
3
+ include Wongi::Engine::DSL
4
+
5
+ dsl {
6
+
7
+ section :make
8
+ clause :test_collector
9
+ action Wongi::Engine::DSL::Action::SimpleCollector.collector
10
+
11
+ }
12
+
13
+ describe 'the engine' do
14
+
15
+ before :each do
16
+ @rete = Wongi::Engine::Network.new
17
+ end
18
+
19
+ def rete
20
+ @rete
21
+ end
22
+
23
+ context 'with a simple generative positive rule' do
24
+
25
+ it 'should generate wmes with an existing rule' do
26
+
27
+ rete << rule('symmetric') {
28
+ forall {
29
+ has :P, "symmetric", true
30
+ has :A, :P, :B
31
+ }
32
+ make {
33
+ gen :B, :P, :A
34
+ }
35
+ }
36
+
37
+ rete << Wongi::Engine::WME.new( "friend", "symmetric", true )
38
+ rete << Wongi::Engine::WME.new( "Alice", "friend", "Bob" )
39
+
40
+ expect(rete.facts.to_a.length).to eq(3)
41
+ expect(rete.facts.select( &:manual? ).length).to eq(2)
42
+ generated = rete.facts.find( &:generated? )
43
+ expect( generated ).to be == Wongi::Engine::WME.new( "Bob", "friend", "Alice" )
44
+ end
45
+
46
+ it 'should generate wmes with an added rule' do
47
+
48
+ rete << Wongi::Engine::WME.new( "friend", "symmetric", true )
49
+ rete << Wongi::Engine::WME.new( "Alice", "friend", "Bob" )
50
+
51
+ expect(rete.facts.to_a.length).to eq(2)
52
+
53
+ rete << rule('symmetric') {
54
+ forall {
55
+ has :P, "symmetric", true
56
+ has :A, :P, :B
57
+ }
58
+ make {
59
+ gen :B, :P, :A
60
+ }
61
+ }
62
+
63
+ expect(rete.facts.to_a.length).to eq(3)
64
+ expect(rete.facts.select( &:manual? ).size).to eq(2)
65
+ end
66
+
67
+ it 'should not get confused by recursive activations' do
68
+
69
+ rete << rule('reflexive') {
70
+ forall {
71
+ has :Predicate, "reflexive", true
72
+ has :X, :Predicate, :Y
73
+ }
74
+ make {
75
+ gen :X, :Predicate, :X
76
+ gen :Y, :Predicate, :Y
77
+ }
78
+ }
79
+
80
+ rete << [:p, "reflexive", true]
81
+ rete << [:x, :p, :y]
82
+
83
+ expect(rete.wmes.to_a.length).to eq(4)
84
+ expect(rete.select(:x, :p, :x).length).to eq(1)
85
+ expect(rete.select(:y, :p, :y).length).to eq(1)
86
+
87
+ end
88
+
89
+ end
90
+
91
+ it 'should check equality' do
92
+
93
+ node = rete << rule('equality') {
94
+ forall {
95
+ fact :A, "same", :B
96
+ same :A, :B
97
+ }
98
+ make {
99
+
100
+ }
101
+ }
102
+
103
+ rete << [ 42, "same", 42 ]
104
+ expect(node.size).to eq(1)
105
+
106
+ end
107
+
108
+ it 'should compare things' do
109
+
110
+ rete << rule('less') {
111
+ forall {
112
+ has :A, :age, :N1
113
+ has :B, :age, :N2
114
+ less :N1, :N2
115
+ }
116
+ make {
117
+ gen :A, :younger, :B
118
+ }
119
+ }
120
+
121
+ rete << rule('less') {
122
+ forall {
123
+ has :A, :age, :N1
124
+ has :B, :age, :N2
125
+ greater :N1, :N2
126
+ }
127
+ make {
128
+ gen :A, :older, :B
129
+ }
130
+ }
131
+
132
+ rete << ["Alice", :age, 42]
133
+ rete << ["Bob", :age, 43]
134
+
135
+ items = rete.select "Alice", :younger, "Bob"
136
+ expect(items.size).to eq(1)
137
+
138
+ items = rete.select "Bob", :older, "Alice"
139
+ expect(items.size).to eq(1)
140
+
141
+ end
142
+
143
+ it 'should use collectors' do
144
+
145
+ rete << rule('collector') {
146
+ forall {
147
+ has :X, :_, 42
148
+ }
149
+ make {
150
+ test_collector :X
151
+ }
152
+ }
153
+
154
+ rete << [ "answer", "is", 42 ]
155
+ rete << [ "question", "is", -1 ]
156
+
157
+ collection = rete.collection(:test_collector)
158
+ expect(collection.size).to eq(1)
159
+ expect(collection.first).to eq("answer")
160
+
161
+ end
162
+
163
+ it "should properly show error messages" do
164
+ rete << rule("Error rule") {
165
+ forall {
166
+ has :_, :_, :TestNumber
167
+ greater :TestNumber, 0
168
+ }
169
+ make {
170
+ error "An error has occurred"
171
+ }
172
+ }
173
+
174
+ rete << [ "A", "B", 1 ]
175
+
176
+ error_messages = rete.errors.map(&:message)
177
+ expect(error_messages).to eq(["An error has occurred"])
178
+ end
179
+
180
+ it 'should use generic collectors' do
181
+
182
+ rete << rule('generic-collector') {
183
+ forall {
184
+ has :X, :_, 42
185
+ }
186
+ make {
187
+ collect :X, :things_that_are_42
188
+ }
189
+ }
190
+
191
+ rete << [ "answer", "is", 42 ]
192
+ rete << [ "question", "is", -1 ]
193
+
194
+ collection = rete.collection(:things_that_are_42)
195
+ expect(collection.size).to eq(1)
196
+ expect(collection.first).to eq("answer")
197
+
198
+ end
199
+
200
+ it 'should accept several rules' do
201
+
202
+ expect {
203
+
204
+ rete << rule('generic-collector') {
205
+ forall {
206
+ has :X, :_, 42
207
+ }
208
+ make {
209
+ collect :X, :things_that_are_42
210
+ }
211
+ }
212
+
213
+ rete << rule('collector') {
214
+ forall {
215
+ has :X, :_, 42
216
+ }
217
+ make {
218
+ test_collector :X
219
+ }
220
+ }
221
+
222
+ }.not_to raise_error
223
+
224
+ end
225
+
226
+ it 'should process negative nodes' do
227
+
228
+ production = (rete << rule('negative') {
229
+ forall {
230
+ neg :_, :_, 42
231
+ }
232
+ })
233
+
234
+ expect(production.size).to eq(1)
235
+
236
+ rete << [ "answer", "is", 42 ]
237
+
238
+ expect(production.size).to eq(0)
239
+
240
+ end
241
+
242
+ context 'queries' do
243
+
244
+ before :each do
245
+ rete << query("test-query") {
246
+ search_on :X
247
+ forall {
248
+ has :X, "is", :Y
249
+ }
250
+ }
251
+ end
252
+
253
+ it 'should run' do
254
+ rete << ["answer", "is", 42]
255
+ rete.execute "test-query", {X: "answer"}
256
+ expect(rete.results["test-query"].size).to eq(1)
257
+ expect(rete.results["test-query"].tokens.first[:Y]).to eq(42)
258
+ end
259
+
260
+ it 'should run several times' do
261
+ rete << ["answer", "is", 42]
262
+ rete << ['question', 'is', '6x9']
263
+ rete.execute "test-query", {X: "answer"}
264
+ rete.execute "test-query", {X: "question"}
265
+ expect(rete.results["test-query"].tokens.to_a.last[:Y]).to eq('6x9')
266
+ expect(rete.results["test-query"].size).to eq(1)
267
+ end
268
+
269
+ end
270
+
271
+ it 'should correctly execute a query several times' do
272
+
273
+ end
274
+
275
+ context 'with timelines' do
276
+
277
+ it 'should not match with no past point' do
278
+
279
+ production = rete.rule {
280
+ forall {
281
+ has 1, 2, 3, time: -1
282
+ }
283
+ }
284
+
285
+ expect(production.size).to eq(0)
286
+
287
+ rete << [1, 2, 3]
288
+
289
+ expect(production.size).to eq(0)
290
+
291
+ end
292
+
293
+ it 'should match a simple past point' do
294
+
295
+ production = rete.rule {
296
+ forall {
297
+ has 1, 2, 3, time: -1
298
+ }
299
+ }
300
+
301
+ rete << [1, 2, 3]
302
+ rete.snapshot!
303
+
304
+ expect(production.size).to eq(1)
305
+
306
+ end
307
+
308
+ context 'using the :asserted clause' do
309
+
310
+ it 'should match asserted items' do
311
+ count = 0
312
+ production = rete.rule do
313
+ forall {
314
+ asserted 1, 2, 3
315
+ }
316
+ make { action { count += 1} }
317
+ end
318
+ expect(production.size).to eq(0)
319
+ rete.snapshot!
320
+ rete << [1, 2, 3]
321
+ expect(production.size).to eq(1)
322
+ #puts count
323
+ end
324
+
325
+ it 'should not match kept items' do
326
+ count = 0
327
+ production = rete.rule do
328
+ forall {
329
+ asserted 1, 2, 3
330
+ }
331
+ make { action { count += 1} }
332
+ end
333
+ rete << [1, 2, 3]
334
+ expect(production.size).to eq(1)
335
+ rete.snapshot!
336
+ expect(production.size).to eq(0)
337
+ #puts count
338
+ end
339
+
340
+ end
341
+
342
+ context 'using the :kept clause' do
343
+
344
+ it 'should match kept items' do
345
+ count = 0
346
+ production = rete.rule do
347
+ forall {
348
+ kept 1, 2, 3
349
+ }
350
+ make { action { count += 1} }
351
+ end
352
+ rete << [1, 2, 3]
353
+ expect(production.size).to eq(0)
354
+ rete.snapshot!
355
+ expect(production.size).to eq(1)
356
+ #puts count
357
+ end
358
+
359
+ it 'should not match asserted wmes' do
360
+ count = 0
361
+ production = rete.rule do
362
+ forall {
363
+ kept 1, 2, 3
364
+ }
365
+ make { action { count += 1} }
366
+ end
367
+ expect(production.size).to eq(0)
368
+ rete.snapshot!
369
+ rete << [1, 2, 3]
370
+ expect(production.size).to eq(0)
371
+ #puts count
372
+ end
373
+
374
+ end
375
+
376
+ end
377
+
378
+ end
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+
3
+ describe Wongi::Engine::Network do
4
+
5
+ let( :engine ) { Wongi::Engine.create }
6
+ subject { engine }
7
+
8
+ it 'should assert facts' do
9
+ subject << [1,2,3]
10
+ expect( subject.select( :_, 2, :_) ).to have(1).item
11
+ end
12
+
13
+ it 'should retract facts' do
14
+ subject << [1,2,3]
15
+ subject.retract [1,2,3]
16
+ expect( subject.select( :_, 2, :_) ).to be_empty
17
+ end
18
+
19
+ it 'asserted facts end up in productions' do
20
+
21
+ prod = subject << rule { forall { has :X, 2, :Z } }
22
+ subject << [1,2,3]
23
+ expect( prod ).to have(1).tokens
24
+
25
+ end
26
+
27
+ it 'retracted facts are removed from productions' do
28
+
29
+ prod = subject << rule { forall { has :X, 2, :Z } }
30
+ subject << [1,2,3]
31
+ subject.retract [1,2,3]
32
+ expect( prod ).to have(0).tokens
33
+
34
+ end
35
+
36
+ it 'retracted facts should trigger deactivation' do
37
+
38
+ activated_z = nil
39
+ deactivated_z = nil
40
+
41
+ prod = subject << rule {
42
+ forall { has :X, 2, :Z }
43
+ make {
44
+ action activate: ->(token) { activated_z = token[:Z] },
45
+ deactivate: ->(token) { deactivated_z = token[:Z] }
46
+ }
47
+ }
48
+ subject << [1,2,3]
49
+ expect( activated_z ).to be == 3
50
+
51
+ subject.retract [1,2,3]
52
+ expect( deactivated_z ).to be == 3
53
+
54
+ end
55
+
56
+ it 'retracted facts should propagate through join chains' do
57
+
58
+ deactivated = nil
59
+
60
+ prod = engine << rule {
61
+ forall {
62
+ has :X, :is, :Y
63
+ has :Y, :is, :Z
64
+ }
65
+ make {
66
+ action deactivate: ->(token) { deactivated = token }
67
+ }
68
+ }
69
+
70
+ engine << [1, :is, 2]
71
+ engine << [2, :is, 3]
72
+
73
+ expect( prod ).to have(1).tokens
74
+
75
+ engine.retract [1, :is, 2]
76
+ expect( prod ).to have(0).tokens
77
+ expect( deactivated[:X] ).to be == 1
78
+ expect( deactivated[:Y] ).to be == 2
79
+ expect( deactivated[:Z] ).to be == 3
80
+
81
+ end
82
+
83
+ it 'retraction should reactivate neg nodes' do
84
+
85
+ prod = engine << rule { forall { neg 1, 2, 3} }
86
+
87
+ expect( prod ).to have(1).tokens
88
+
89
+ engine << [1, 2 ,3]
90
+ expect( prod ).to have(0).tokens
91
+
92
+ engine.retract [1, 2, 3]
93
+ expect( prod ).to have(1).tokens
94
+
95
+ end
96
+
97
+ describe 'retraction with neg nodes lower in the chain' do
98
+
99
+ def expect_tokens n
100
+ expect( prod ).to have(n).tokens
101
+ end
102
+
103
+ before :each do
104
+
105
+ engine << rule('retract') {
106
+ forall {
107
+ has :x, :u, :Y
108
+ neg :Y, :w, :_
109
+ }
110
+ }
111
+
112
+ end
113
+
114
+ let( :prod ) { engine.productions['retract'] }
115
+
116
+ specify 'case 1' do
117
+
118
+ engine << [:x, :u, :y]
119
+ expect_tokens 1
120
+
121
+ engine << [:y, :w, :z]
122
+ expect_tokens 0
123
+
124
+ engine.retract [:y, :w, :z]
125
+ expect_tokens 1
126
+
127
+ engine.retract [:x, :u, :y]
128
+ expect_tokens 0
129
+
130
+ end
131
+
132
+ specify 'case 2' do
133
+
134
+ engine << [:x, :u, :y]
135
+ expect_tokens 1
136
+
137
+ engine << [:y, :w, :z]
138
+ expect_tokens 0
139
+
140
+ engine.retract [:x, :u, :y]
141
+ expect_tokens 0
142
+
143
+ engine.retract [:y, :w, :z]
144
+ expect_tokens 0
145
+
146
+ end
147
+
148
+ specify 'case 3' do
149
+
150
+ engine << [:y, :w, :z]
151
+ expect_tokens 0
152
+
153
+ engine << [:x, :u, :y]
154
+ expect_tokens 0
155
+
156
+ engine.retract [:x, :u, :y]
157
+ expect_tokens 0
158
+
159
+ engine.retract [:y, :w, :z]
160
+ expect_tokens 0
161
+
162
+ end
163
+
164
+ specify 'case 4' do
165
+
166
+ engine << [:y, :w, :z]
167
+ expect_tokens 0
168
+
169
+ engine << [:x, :u, :y]
170
+ expect_tokens 0
171
+
172
+ engine.retract [:y, :w, :z]
173
+ expect_tokens 1
174
+
175
+ engine.retract [:x, :u, :y]
176
+ expect_tokens 0
177
+
178
+ end
179
+
180
+ end
181
+
182
+ end