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

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fab0e9a6fff37b3dc1a6cb4e7aa2c10ea08c82dbe78c874a7d475a18dfef170
4
- data.tar.gz: e23cedb3481395ff240868b45eeedc5854331f521c3c00e96ff6e537d2e7c7e5
3
+ metadata.gz: e9461adc4b75933300d9be090ac8825c90ddc2b5ba167ec7e47128e4622aa853
4
+ data.tar.gz: ed1f2760dae84df0faee9d8ccc80849713b849299f2b4bc5520d8004f332879f
5
5
  SHA512:
6
- metadata.gz: 9344af4983e233a95b157b4877f432f17fc7068a6ae77de4ca4edb5678e3555fb079dc13c8dee1866b21a09afcb4c003077ff8e70d7ad1222605ceab828d7726
7
- data.tar.gz: cda25e408f9ba4706002d476f1c03cc367d10dddaf6381736b27f17be9b9e6b43e2993c9d84947d8eee3e04d08e631ba79a31332948f08b7ee44ecf95fd17ffc
6
+ metadata.gz: c3f6c9b2f491c96758c319268b61e7654dcdeab9d0acfd41c582428245fcd5b52edd2f8fa34674882600b9ce6668df6ab48de11123fcf72819d7a9d53aa4e9a4
7
+ data.tar.gz: fd2e4ac006478621202f2013d43e022186e48e3411c035e9d7f2a9c6557001b17125519a2280092507ca749ef065aa43f6f69b194e37393ce365f7ae7d5e1ee2
@@ -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
@@ -40,16 +40,15 @@ module Wongi::Engine
40
40
  return false if token.wme == wme
41
41
 
42
42
  # asserting this WME would invalidate the match
43
- return false if token.node.is_a?(NegNode) && token.node.matches?(token, wme)
43
+ # TODO: clean up
44
+ return false if token.node.is_a?(NegNode) && wme =~ token.node.alpha.template && token.node.matches?(token, wme) # how much is actually necessary?
44
45
 
45
- if token.parent && !considered_tokens.include?(token.parent)
46
- tokens_to_consider.push(token.parent)
47
- end
46
+ (token.parents - considered_tokens).each { |parent| tokens_to_consider.push(parent) }
47
+
48
+ next unless token.wme
48
49
 
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
50
+ overlay.generators(token.wme).each do |generator|
51
+ tokens_to_consider.push(generator.token) unless considered_tokens.include?(generator.token)
53
52
  end
54
53
  end
55
54
 
@@ -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-alpha8".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,14 +1,14 @@
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.alpha8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Valeri Sokolov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-02 00:00:00.000000000 Z
11
+ date: 2022-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry