wongi-engine 0.4.0.pre.alpha6 → 0.4.0.pre.alpha7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fab0e9a6fff37b3dc1a6cb4e7aa2c10ea08c82dbe78c874a7d475a18dfef170
4
- data.tar.gz: e23cedb3481395ff240868b45eeedc5854331f521c3c00e96ff6e537d2e7c7e5
3
+ metadata.gz: 600fe3fc20d2cbecf1f59d7ad771eb387f97c9f00144089961dacb247c817475
4
+ data.tar.gz: 94d3cd24972fd5b6b67a1940804dc30d91c960e05ffe143889b5e0bb99e1fb84
5
5
  SHA512:
6
- metadata.gz: 9344af4983e233a95b157b4877f432f17fc7068a6ae77de4ca4edb5678e3555fb079dc13c8dee1866b21a09afcb4c003077ff8e70d7ad1222605ceab828d7726
7
- data.tar.gz: cda25e408f9ba4706002d476f1c03cc367d10dddaf6381736b27f17be9b9e6b43e2993c9d84947d8eee3e04d08e631ba79a31332948f08b7ee44ecf95fd17ffc
6
+ metadata.gz: f233a3b5cd1a10ffab78742c5f14f319c24ff64e2d2683cf4e41e304287c832e818802e4224316e460909975b93658c0e13ecef5888101841002c054ea10689a
7
+ data.tar.gz: 11b1c635fa38aa4427dac9eba6807ad8294613a814ac98f588c0eee0eb7febec0542192e7113ea6b02bccffb25063e698e609a6fd1687a48cefbb5fb3a1b4d2f
@@ -12,15 +12,9 @@ module Wongi::Engine
12
12
  end
13
13
 
14
14
  def make_partition_fn(partition)
15
- return nil if partition.nil?
15
+ return nil if partition.empty?
16
16
 
17
- if Template.variable?(partition)
18
- ->(token) { token[partition] }
19
- elsif partition.is_a?(Array) && partition.all? { Template.variable?(_1) }
20
- ->(token) { token.values_at(*partition) }
21
- else
22
- partition
23
- end
17
+ ->(token) { token.values_at(*partition) }
24
18
  end
25
19
 
26
20
  def make_aggregate_fn(agg)
@@ -53,12 +47,16 @@ module Wongi::Engine
53
47
  end
54
48
 
55
49
  def evaluate(child: nil)
56
- groups = if partition
57
- tokens.group_by(&partition).values
58
- else
59
- # just a single group of everything
60
- [tokens]
61
- end
50
+ return if tokens.empty?
51
+
52
+ groups =
53
+ if partition
54
+ tokens.group_by(&partition).values
55
+ else
56
+ # just a single group of everything
57
+ [tokens]
58
+ end
59
+
62
60
 
63
61
  groups.each do |tokens|
64
62
  aggregated = self.aggregate.call(tokens.map(&self.map))
@@ -67,9 +65,9 @@ module Wongi::Engine
67
65
  tokens.each do |token|
68
66
  # TODO: optimize this to work with a diff of actual changes
69
67
  beta_deactivate_children(token: token, children: children)
70
- children.each do |beta|
71
- beta.beta_activate(Token.new(beta, token, nil, assignment))
72
- end
68
+ end
69
+ children.each do |beta|
70
+ beta.beta_activate(Token.new(beta, tokens, nil, assignment))
73
71
  end
74
72
  end
75
73
  end
@@ -20,7 +20,7 @@ module Wongi::Engine
20
20
  overlay.remove_token(token)
21
21
  children.each do |child|
22
22
  child.tokens.each do |t|
23
- if t.parent == token
23
+ if t.child_of?(token)
24
24
  child.beta_deactivate t
25
25
  # token.destroy
26
26
  end
@@ -51,7 +51,7 @@ module Wongi::Engine
51
51
 
52
52
  def beta_deactivate_children(token: nil, wme: nil, children: self.children)
53
53
  children.each do |child|
54
- child.tokens.select { (token.nil? || _1.parent == token) && (wme.nil? || _1.wme == wme) }.each do |child_token|
54
+ child.tokens.select { (token.nil? || _1.child_of?(token)) && (wme.nil? || _1.wme == wme) }.each do |child_token|
55
55
  child.beta_deactivate(child_token)
56
56
  end
57
57
  end
@@ -36,7 +36,7 @@ module Wongi
36
36
  def ncc_deactivate(token)
37
37
  # p ncc_deactivate: {class: self.class, object_id:, token:}
38
38
  children.each do |beta|
39
- beta.tokens.select { |t| t.parent == token }.each do |t|
39
+ beta.tokens.select { |t| t.child_of?(token) }.each do |t|
40
40
  beta.beta_deactivate t
41
41
  end
42
42
  end
@@ -23,7 +23,7 @@ module Wongi
23
23
  if optional
24
24
  # we're going to change the optional state so the old ones need to be removed
25
25
  child.tokens.each do |ct|
26
- child.beta_deactivate(ct) if ct.parent == token
26
+ child.beta_deactivate(ct) if ct.child_of?(token)
27
27
  end
28
28
  end
29
29
  child.beta_activate Token.new(child, token, wme, assignments)
@@ -43,7 +43,7 @@ module Wongi
43
43
 
44
44
  children.each do |child|
45
45
  child.tokens.each do |ct|
46
- child.beta_deactivate(ct) if ct.parent == token
46
+ child.beta_deactivate(ct) if ct.child_of?(token)
47
47
  end
48
48
  child.beta_activate Token.new(child, token, nil, {})
49
49
  end
@@ -32,7 +32,7 @@ module Wongi::Engine
32
32
  overlay.remove_token(token)
33
33
 
34
34
  children.each do |child|
35
- child.tokens.select { _1.parent == token }.each { child.beta_deactivate(_1) }
35
+ child.tokens.select { _1.child_of?(token) }.each { child.beta_deactivate(_1) }
36
36
  end
37
37
 
38
38
  nil
@@ -42,14 +42,12 @@ module Wongi::Engine
42
42
  # asserting this WME would invalidate the match
43
43
  return false if token.node.is_a?(NegNode) && token.node.matches?(token, wme)
44
44
 
45
- if token.parent && !considered_tokens.include?(token.parent)
46
- tokens_to_consider.push(token.parent)
47
- end
45
+ (token.parents - considered_tokens).each { |parent| tokens_to_consider.push(parent) }
46
+
47
+ next unless token.wme
48
48
 
49
- if token.wme
50
- overlay.generators(token.wme).each do |generator|
51
- tokens_to_consider.push(generator.token) unless considered_tokens.include?(generator.token)
52
- end
49
+ overlay.generators(token.wme).each do |generator|
50
+ tokens_to_consider.push(generator.token) unless considered_tokens.include?(generator.token)
53
51
  end
54
52
  end
55
53
 
@@ -6,9 +6,10 @@ module Wongi::Engine
6
6
  def initialize(var, options = {})
7
7
  @var = var
8
8
  @over = options[:over]
9
- @partition = options[:partition]
9
+ @partition = Array(options[:partition])
10
10
  @aggregate = options[:using]
11
11
  @map = options[:map]
12
+ raise "can only partition by variables" unless partition.all? { |p| Template.variable?(p) }
12
13
  end
13
14
 
14
15
  def compile(context)
@@ -254,7 +254,7 @@ module Wongi::Engine
254
254
  end
255
255
  end
256
256
 
257
- def select(s, p, o)
257
+ def select(s, p, o, &block)
258
258
  matching = current_overlay.select(s, p, o)
259
259
  if block_given?
260
260
  matching.each(&block)
@@ -1,26 +1,25 @@
1
+ require 'set'
2
+
1
3
  module Wongi::Engine
2
4
  class Token
3
- attr_reader :children, :wme, :node, :generated_wmes
4
- attr_accessor :parent
5
+ attr_reader :wme, :node, :generated_wmes, :parents
5
6
 
6
- def initialize(node, token, wme, assignments = {})
7
+ def initialize(node, parents, wme, assignments = {})
7
8
  @node = node
8
- @parent = token
9
+ @parents = Set.new(Array(parents))
9
10
  @wme = wme
10
11
  @assignments = assignments
11
- @children = []
12
12
  @deleted = false
13
13
  @ncc_results = []
14
14
  @generated_wmes = []
15
- token.children << self if token
16
15
  end
17
16
 
18
17
  def ancestors
19
- if parent
20
- parent.ancestors.unshift parent
21
- else
22
- []
23
- end
18
+ parents.flat_map(&:ancestors).uniq + parents.to_a
19
+ end
20
+
21
+ def child_of?(token)
22
+ parents.include?(token)
24
23
  end
25
24
 
26
25
  def subst(variable, value)
@@ -50,7 +49,10 @@ module Wongi::Engine
50
49
 
51
50
  # TODO: ignore assignments?
52
51
  def duplicate?(other)
53
- parent.equal?(other.parent) && @wme.equal?(other.wme) && assignments == other.assignments
52
+ instance_of?(other.class) &&
53
+ parents == other.parents &&
54
+ wme == other.wme &&
55
+ assignments == other.assignments
54
56
  end
55
57
 
56
58
  def to_s
@@ -75,22 +77,18 @@ module Wongi::Engine
75
77
  end
76
78
 
77
79
  # for neg feedback loop protection
78
- def generated?(wme)
79
- return true if generated_wmes.any? { |w| w == wme }
80
-
81
- children.any? { |t| t.generated? wme }
82
- end
80
+ # def generated?(wme)
81
+ # return true if generated_wmes.any? { |w| w == wme }
82
+ #
83
+ # children.any? { |t| t.generated? wme }
84
+ # end
83
85
 
84
86
  protected
85
87
 
86
88
  def all_assignments
87
- raise "Assignments is not a hash" unless @assignments.is_a?(Hash)
88
-
89
- if @parent
90
- @parent.assignments.merge @assignments
91
- else
92
- @assignments
93
- end
89
+ parents.each_with_object({}) do |parent, acc|
90
+ acc.merge!(parent.assignments)
91
+ end.merge(@assignments)
94
92
  end
95
93
  end
96
94
 
@@ -99,7 +97,7 @@ module Wongi::Engine
99
97
  @parent = token
100
98
  @wme = wme
101
99
  @assignments = assignments
102
- @children = []
100
+ # @children = []
103
101
  @neg_join_results = []
104
102
  @opt_join_results = []
105
103
  @ncc_results = []
@@ -1,5 +1,5 @@
1
1
  module Wongi
2
2
  module Engine
3
- VERSION = "0.4.0-alpha6".freeze
3
+ VERSION = "0.4.0-alpha7".freeze
4
4
  end
5
5
  end
@@ -10,10 +10,6 @@ module Wongi::Engine
10
10
  self.class.new(subject, predicate, object)
11
11
  end
12
12
 
13
- def ==(other)
14
- other && subject == other.subject && predicate == other.predicate && object == other.object
15
- end
16
-
17
13
  # @param template Wongi::Engine::Template
18
14
  def =~(template)
19
15
  raise Wongi::Engine::Error, "Cannot match a WME against a #{template.class}" unless template.is_a?(Template)
@@ -30,14 +26,6 @@ module Wongi::Engine
30
26
  inspect
31
27
  end
32
28
 
33
- def hash
34
- @hash ||= [subject.hash, predicate.hash, object.hash].hash
35
- end
36
-
37
- def eql?(other)
38
- subject.eql?(other.subject) && predicate.eql?(other.predicate) && object.eql?(other.object)
39
- end
40
-
41
29
  protected
42
30
 
43
31
  def match_member(mine, theirs)
@@ -20,24 +20,21 @@ describe 'aggregate' do
20
20
 
21
21
  engine << [:apple, :weight, 5]
22
22
  expect(production.size).to be == 1
23
- production.tokens.each do |token|
24
- expect(token[:X]).to be == 5
25
- expect(token[:Fruit]).to be == :apple
26
- end
23
+ token = production.tokens.first
24
+ expect(token[:X]).to be == 5
25
+ expect(token[:Fruit]).to be == :apple
27
26
 
28
27
  engine << [:pea, :weight, 2]
29
- expect(production.size).to be == 2
30
- production.tokens.each do |token|
31
- expect(token[:X]).to be == 2
32
- expect(token[:Fruit]).to be == :pea
33
- end
28
+ expect(production.size).to be == 1
29
+ token = production.tokens.first
30
+ expect(token[:X]).to be == 2
31
+ expect(token[:Fruit]).to be == :pea
34
32
 
35
33
  engine.retract [:pea, :weight, 2]
36
34
  expect(production.size).to be == 1
37
- production.tokens.each do |token|
38
- expect(token[:X]).to be == 5
39
- expect(token[:Fruit]).to be == :apple
40
- end
35
+ token = production.tokens.first
36
+ expect(token[:X]).to be == 5
37
+ expect(token[:Fruit]).to be == :apple
41
38
  end
42
39
  end
43
40
 
@@ -53,24 +50,21 @@ describe 'aggregate' do
53
50
 
54
51
  engine << [:pea, :weight, 2]
55
52
  expect(production.size).to be == 1
56
- production.tokens.each do |token|
57
- expect(token[:X]).to be == 2
58
- expect(token[:Fruit]).to be == :pea
59
- end
53
+ token = production.tokens.first
54
+ expect(token[:X]).to be == 2
55
+ expect(token[:Fruit]).to be == :pea
60
56
 
61
57
  engine << [:apple, :weight, 5]
62
- expect(production.size).to be == 2
63
- production.tokens.each do |token|
64
- expect(token[:X]).to be == 5
65
- expect(token[:Fruit]).to be == :apple
66
- end
58
+ expect(production.size).to be == 1
59
+ token = production.tokens.first
60
+ expect(token[:X]).to be == 5
61
+ expect(token[:Fruit]).to be == :apple
67
62
 
68
63
  engine.retract [:apple, :weight, 5]
69
64
  expect(production.size).to be == 1
70
- production.tokens.each do |token|
71
- expect(token[:X]).to be == 2
72
- expect(token[:Fruit]).to be == :pea
73
- end
65
+ token = production.tokens.first
66
+ expect(token[:X]).to be == 2
67
+ expect(token[:Fruit]).to be == :pea
74
68
  end
75
69
  end
76
70
 
@@ -85,27 +79,23 @@ describe 'aggregate' do
85
79
 
86
80
  engine << [:pea, :weight, 1]
87
81
  expect(production.size).to be == 1
88
- production.tokens.each do |token|
89
- expect(token[:Count]).to be == 1
90
- end
82
+ token = production.tokens.first
83
+ expect(token[:Count]).to be == 1
91
84
 
92
85
  engine << [:apple, :weight, 5]
93
- expect(production.size).to be == 2
94
- production.tokens.each do |token|
95
- expect(token[:Count]).to be == 2
96
- end
86
+ expect(production.size).to be == 1
87
+ token = production.tokens.first
88
+ expect(token[:Count]).to be == 2
97
89
 
98
90
  engine << [:watermelon, :weight, 15]
99
- expect(production.size).to be == 3
100
- production.tokens.each do |token|
101
- expect(token[:Count]).to be == 3
102
- end
91
+ expect(production.size).to be == 1
92
+ token = production.tokens.first
93
+ expect(token[:Count]).to be == 3
103
94
 
104
95
  engine.retract [:apple, :weight, 5]
105
- expect(production.size).to be == 2
106
- production.tokens.each do |token|
107
- expect(token[:Count]).to be == 2
108
- end
96
+ expect(production.size).to be == 1
97
+ token = production.tokens.first
98
+ expect(token[:Count]).to be == 2
109
99
  end
110
100
 
111
101
  it 'works with a post-filter' do
@@ -124,10 +114,9 @@ describe 'aggregate' do
124
114
  expect(production.size).to be == 0
125
115
 
126
116
  engine << [:watermelon, :weight, 15]
127
- expect(production.size).to be == 3
128
- production.tokens.each do |token|
129
- expect(token[:Count]).to be == 3
130
- end
117
+ expect(production.size).to be == 1
118
+ token = production.tokens.first
119
+ expect(token[:Count]).to be == 3
131
120
 
132
121
  engine.retract [:apple, :weight, 5]
133
122
  expect(production.size).to be == 0
@@ -148,7 +137,7 @@ describe 'aggregate' do
148
137
  engine << [:factor, 12, 3]
149
138
  engine << [:factor, 12, 4]
150
139
 
151
- expect(production).to have(4).tokens
140
+ expect(production).to have(2).tokens
152
141
  production.tokens.each do |token|
153
142
  expect(token[:Product]).to be_a(Integer)
154
143
  expect(token[:Product]).to eq(token[:Number])
@@ -170,11 +159,55 @@ describe 'aggregate' do
170
159
  engine << [:factor, 12, 3]
171
160
  engine << [:factor, 12, 4]
172
161
 
173
- expect(production).to have(4).tokens
162
+ expect(production).to have(2).tokens
174
163
  production.tokens.each do |token|
175
164
  expect(token[:Product]).to be_a(Integer)
176
165
  expect(token[:Product]).to eq(token[:Number])
177
166
  end
178
167
  end
179
168
  end
169
+
170
+ it 'propagates a single token' do
171
+ engine << rule {
172
+ forall {
173
+ has :Item, :price_group, :Group
174
+ has :Group, :name, :Name
175
+ has :Group, :base_price, :Price
176
+ max :MaxPrice, over: :Price, partition: %i[Item Name]
177
+ # equal :Price, :MaxPrice # -- this is necessary for this to work without token converging
178
+ }
179
+ make {
180
+ gen :Group, :price, :MaxPrice
181
+ }
182
+ }
183
+
184
+ engine << rule("sum") {
185
+ forall {
186
+ has :Item, :price_group, :Group
187
+ has :Group, :price, :Price
188
+ sum :TotalPrice, over: :Price, partition: :Item
189
+ }
190
+ make {
191
+ gen :Item, :price, :TotalPrice
192
+ }
193
+ }
194
+
195
+ material1 = Object.new
196
+ engine << [:toy, :price_group, material1]
197
+ engine << [material1, :name, :material]
198
+ engine << [material1, :base_price, 100]
199
+
200
+ material2 = Object.new
201
+ engine << [:toy, :price_group, material2]
202
+ engine << [material2, :name, :material]
203
+ engine << [material2, :base_price, 200]
204
+
205
+ packaging = Object.new
206
+ engine << [:toy, :price_group, packaging]
207
+ engine << [packaging, :name, :packaging]
208
+ engine << [packaging, :base_price, 20]
209
+
210
+ total_price = engine.select(:toy, :price, :_).first
211
+ expect(total_price.object).to eq(220)
212
+ end
180
213
  end
data/wongi-engine.gemspec CHANGED
@@ -20,11 +20,10 @@ Gem::Specification.new do |gem|
20
20
 
21
21
  gem.homepage = 'https://github.com/ulfurinn/wongi-engine'
22
22
  gem.metadata = {
23
- "documentation_uri" => 'https://ulfurinn.github.io/wongi-engine/',
23
+ "documentation_uri" => 'https://ulfurinn.github.io/wongi-engine/',
24
24
  "rubygems_mfa_required" => 'true',
25
25
  }
26
26
 
27
-
28
27
  gem.files = if GemHelper.git?
29
28
  `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
30
29
  else
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wongi-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0.pre.alpha6
4
+ version: 0.4.0.pre.alpha7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valeri Sokolov