bud 0.9.4 → 0.9.5

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.
@@ -6,7 +6,7 @@ class DepAnalysis #:nodoc: all
6
6
 
7
7
  state do
8
8
  # Data inserted by client, usually from t_depends and t_provides
9
- scratch :depends, [:lhs, :op, :body, :neg]
9
+ scratch :depends, [:lhs, :op, :body, :neg, :in_body]
10
10
  scratch :providing, [:pred, :input]
11
11
 
12
12
  # Intermediate state
@@ -36,7 +36,7 @@ class DepAnalysis #:nodoc: all
36
36
 
37
37
  cycle <= depends_tc do |d|
38
38
  if d.lhs == d.body
39
- unless d.neg and !d.temporal
39
+ unless d.neg and !d.temporal
40
40
  [d.lhs, d.via, d.neg, d.temporal]
41
41
  end
42
42
  end
@@ -60,11 +60,10 @@ class DepAnalysis #:nodoc: all
60
60
  [p.pred, true]
61
61
  end
62
62
  else
63
- unless depends_tc.map{|d| d.lhs if d.lhs != d.body}.include? p.pred
63
+ unless depends_tc.map{|dt| dt.lhs if dt.lhs != dt.body}.include? p.pred
64
64
  [p.pred, false]
65
65
  end
66
66
  end
67
67
  end
68
68
  end
69
69
  end
70
-
@@ -1,4 +1,3 @@
1
- require 'set'
2
1
  require 'bud/collections'
3
2
 
4
3
  module Bud
@@ -13,7 +12,7 @@ module Bud
13
12
  class PushElement < BudCollection
14
13
  attr_accessor :rescan, :invalidated
15
14
  attr_accessor :elem_name
16
- attr_reader :found_delta, :refcount, :wired_by, :outputs
15
+ attr_reader :found_delta, :wired_by, :outputs
17
16
 
18
17
  def initialize(name_in, bud_instance, collection_name=nil, given_schema=nil, defer_schema=false, &blk)
19
18
  super(name_in, bud_instance, given_schema, defer_schema)
@@ -25,7 +24,6 @@ module Bud
25
24
  @wired_by = []
26
25
  @elem_name = name_in
27
26
  @found_delta = false
28
- @refcount = 1
29
27
  @collection_name = collection_name
30
28
  @invalidated = true
31
29
  @rescan = true
@@ -58,6 +56,8 @@ module Bud
58
56
  print "#{next_accum} "
59
57
  if o.class <= Bud::BudCollection
60
58
  puts "#{(o.object_id*2).to_s(16)}: #{o.qualified_tabname} (#{o.class})"
59
+ elsif o.class <= Bud::LatticeWrapper
60
+ puts "#{o.inspect}"
61
61
  else
62
62
  puts "#{(o.object_id*2).to_s(16)}: (#{o.class.name})"
63
63
  end
@@ -83,17 +83,15 @@ module Bud
83
83
 
84
84
  case kind
85
85
  when :output
86
- raise Bud::Error unless element.respond_to? :insert
87
86
  @outputs << element
88
87
  when :pending
89
- raise Bud::Error unless element.respond_to? :pending_merge
90
88
  @pendings << element
91
89
  when :delete
92
- raise Bud::Error unless element.respond_to? :pending_delete
93
90
  @deletes << element
94
91
  when :delete_by_key
95
- raise Bud::Error unless element.respond_to? :pending_delete_keys
96
92
  @delete_keys << element
93
+ else
94
+ raise Bud::Error, "unrecognized wiring kind: #{kind}"
97
95
  end
98
96
 
99
97
  element.wired_by << self if element.respond_to? :wired_by
@@ -116,27 +114,36 @@ module Bud
116
114
  end
117
115
 
118
116
  def push_out(item, do_block=true)
119
- if item
120
- if do_block && @blk
121
- item = item.to_a if @blk.arity > 1
122
- item = @blk.call item
117
+ return if item.nil?
118
+
119
+ if do_block && @blk
120
+ item = item.to_a if @blk.arity > 1
121
+ item = @blk.call item
122
+ return if item.nil?
123
+ end
124
+
125
+ @outputs.each do |ou|
126
+ if ou.class <= Bud::PushElement
127
+ ou.insert(item, self)
128
+ elsif ou.class <= Bud::BudCollection
129
+ ou.do_insert(item, ou.new_delta)
130
+ elsif ou.class <= Bud::LatticeWrapper
131
+ ou.insert(item, self)
132
+ else
133
+ raise Bud::Error, "expected output target: #{ou.class}"
123
134
  end
135
+ end
124
136
 
125
- unless item.nil?
126
- @outputs.each do |ou|
127
- if ou.class <= Bud::PushElement
128
- ou.insert(item, self)
129
- elsif ou.class <= Bud::BudCollection
130
- ou.do_insert(item, ou.new_delta)
131
- else
132
- raise Bud::Error, "expected either a PushElement or a BudCollection"
133
- end
134
- end
137
+ # for the following, o is a BudCollection
138
+ @deletes.each{|o| o.pending_delete([item])}
139
+ @delete_keys.each{|o| o.pending_delete_keys([item])}
135
140
 
136
- # for all the following, o is a BudCollection
137
- @deletes.each{|o| o.pending_delete([item])}
138
- @delete_keys.each{|o| o.pending_delete_keys([item])}
139
- @pendings.each{|o| o.pending_merge([item])}
141
+ # o is a LatticeWrapper or a BudCollection
142
+ @pendings.each do |o|
143
+ if o.class <= Bud::LatticeWrapper
144
+ o <+ item
145
+ else
146
+ o.pending_merge([item])
140
147
  end
141
148
  end
142
149
  end
@@ -213,34 +220,30 @@ module Bud
213
220
  @collection_name)
214
221
  elem.set_block(&blk)
215
222
  self.wire_to(elem)
216
- toplevel.push_elems[[self.object_id, :each, blk]] = elem
223
+ toplevel.push_elems[[self.object_id, :each_with_index, blk]] = elem
217
224
  end
218
225
 
219
226
  def join(elem2, &blk)
220
- # cached = @bud_instance.push_elems[[self.object_id,:join,[self,elem2], @bud_instance, blk]]
221
- # if cached.nil?
222
- elem2 = elem2.to_push_elem unless elem2.class <= PushElement
223
- toplevel = @bud_instance.toplevel
224
- join = Bud::PushSHJoin.new([self, elem2], toplevel.this_rule_context, [])
225
- self.wire_to(join)
226
- elem2.wire_to(join)
227
- toplevel.push_elems[[self.object_id, :join, [self, elem2], toplevel, blk]] = join
228
- toplevel.push_joins[toplevel.this_stratum] << join
229
- # else
230
- # cached.refcount += 1
231
- # end
232
- return toplevel.push_elems[[self.object_id, :join, [self, elem2], toplevel, blk]]
227
+ elem2 = elem2.to_push_elem unless elem2.kind_of? PushElement
228
+ toplevel = @bud_instance.toplevel
229
+ join = Bud::PushSHJoin.new([self, elem2], toplevel.this_rule_context, [])
230
+ self.wire_to(join)
231
+ elem2.wire_to(join)
232
+ toplevel.push_elems[[self.object_id, :join, [self, elem2], toplevel, blk]] = join
233
+ toplevel.push_joins[toplevel.this_stratum] << join
234
+ return join
233
235
  end
234
236
  def *(elem2, &blk)
235
237
  join(elem2, &blk)
236
238
  end
237
239
 
238
- def notin(elem2, preds=nil, &blk)
240
+ def notin(elem2, *preds, &blk)
241
+ elem2 = elem2.to_push_elem unless elem2.kind_of? PushElement
239
242
  toplevel = @bud_instance.toplevel
240
243
  notin_elem = Bud::PushNotIn.new([self, elem2], toplevel.this_rule_context, preds, &blk)
241
244
  self.wire_to(notin_elem)
242
245
  elem2.wire_to(notin_elem)
243
- toplevel.push_elems[[self.object_id, :notin, collection, toplevel, blk]] = notin_elem
246
+ toplevel.push_elems[[self.object_id, :notin, [self, elem2], toplevel, blk]] = notin_elem
244
247
  return notin_elem
245
248
  end
246
249
 
@@ -280,14 +283,12 @@ module Bud
280
283
  the_schema = { keynames => aggcols }
281
284
  end
282
285
 
283
- aggpairs = aggpairs.map{|ap| ap[1].nil? ? [ap[0]] : [ap[0], canonicalize_col(ap[1])]}
286
+ aggpairs = prep_aggpairs(aggpairs)
284
287
  toplevel = @bud_instance.toplevel
285
- # if @bud_instance.push_elems[[self.object_id, :group, keycols, aggpairs, blk]].nil?
286
- g = Bud::PushGroup.new('grp'+Time.new.tv_usec.to_s, toplevel.this_rule_context, @collection_name, keycols, aggpairs, the_schema, &blk)
287
- self.wire_to(g)
288
- toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]] = g
289
- # end
290
- # toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]]
288
+ g = Bud::PushGroup.new('grp'+Time.new.tv_usec.to_s, toplevel.this_rule_context,
289
+ @collection_name, keycols, aggpairs, the_schema, &blk)
290
+ self.wire_to(g)
291
+ toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]] = g
291
292
  return g
292
293
  end
293
294
 
@@ -305,19 +306,17 @@ module Bud
305
306
  end
306
307
  end
307
308
  aggpairs = [[agg, collection]]
308
- # if toplevel.push_elems[[self.object_id,:argagg, gbkey_cols, aggpairs, blk]].nil?
309
- aa = Bud::PushArgAgg.new('argagg'+Time.new.tv_usec.to_s, toplevel.this_rule_context, @collection_name, gbkey_cols, aggpairs, schema, &blk)
310
- self.wire_to(aa)
311
- toplevel.push_elems[[self.object_id,:argagg, gbkey_cols, aggpairs, blk]] = aa
312
- # end
313
- # return toplevel.push_elems[[self.object_id,:argagg, gbkey_cols, aggpairs, blk]]
309
+ aa = Bud::PushArgAgg.new('argagg'+Time.new.tv_usec.to_s, toplevel.this_rule_context,
310
+ @collection_name, gbkey_cols, aggpairs, schema, &blk)
311
+ self.wire_to(aa)
312
+ toplevel.push_elems[[self.object_id, :argagg, gbkey_cols, aggpairs, blk]] = aa
314
313
  return aa
315
314
  end
316
315
  def argmax(gbcols, col, &blk)
317
- argagg(gbcols, Bud::max(col), blk)
316
+ argagg(:max, gbcols, col, &blk)
318
317
  end
319
318
  def argmin(gbcols, col, &blk)
320
- argagg(gbcols, Bud::min(col), blk)
319
+ argagg(:min, gbcols, col, &blk)
321
320
  end
322
321
  def sort(name=nil, bud_instance=nil, the_schema=nil, &blk)
323
322
  elem = Bud::PushSort.new(name, bud_instance, the_schema, &blk)
@@ -447,6 +446,7 @@ module Bud
447
446
  class ScannerElement < PushElement
448
447
  attr_reader :collection
449
448
  attr_reader :rescan_set, :invalidate_set
449
+ attr_accessor :force_rescan
450
450
 
451
451
  def initialize(elem_name, bud_instance, collection_in,
452
452
  the_schema=collection_in.schema, &blk)
@@ -454,6 +454,7 @@ module Bud
454
454
  @collection = collection_in
455
455
  @rescan_set = []
456
456
  @invalidate_set = []
457
+ @force_rescan = false
457
458
  end
458
459
 
459
460
  def rescan
@@ -485,7 +486,11 @@ module Bud
485
486
  end
486
487
 
487
488
  def scan(first_iter)
488
- if first_iter
489
+ if @force_rescan
490
+ # Scan entire storage
491
+ @collection.each_raw {|item| push_out(item)}
492
+ @force_rescan = false
493
+ elsif first_iter
489
494
  if rescan
490
495
  # Scan entire storage
491
496
  @collection.each_raw {|item| push_out(item)}
@@ -1,5 +1,4 @@
1
1
  require 'bud/executor/elements'
2
- require 'set'
3
2
 
4
3
  module Bud
5
4
  class PushGroup < PushStatefulElement
@@ -10,21 +9,25 @@ module Bud
10
9
  else
11
10
  @keys = keys_in.map{|k| k[1]}
12
11
  end
13
- # An aggpair is an array: [agg class instance, index of input field].
14
- # ap[1] is nil for Count.
15
- @aggpairs = aggpairs_in.map{|ap| [ap[0], ap[1].nil? ? nil : ap[1][1]]}
12
+ # An aggpair is an array: [agg class instance, array of indexes of input
13
+ # agg input columns]. The second field is nil for Count.
14
+ @aggpairs = aggpairs_in.map do |ap|
15
+ agg, *rest = ap
16
+ if rest.empty?
17
+ [agg, nil]
18
+ else
19
+ [agg, rest.map {|r| r[1]}]
20
+ end
21
+ end
16
22
  @groups = {}
17
23
 
18
24
  # Check whether we need to eliminate duplicates from our input (we might
19
25
  # see duplicates because of the rescan/invalidation logic, as well as
20
26
  # because we don't do duplicate elimination on the output of a projection
21
27
  # operator). We don't need to dupelim if all the args are exemplary.
22
- @elim_dups = @aggpairs.any? {|a| not a[0].kind_of? ArgExemplary}
23
- if @elim_dups
24
- @input_cache = Set.new
25
- end
28
+ @elim_dups = @aggpairs.any? {|ap| not ap[0].kind_of? ArgExemplary}
29
+ @input_cache = Set.new if @elim_dups
26
30
 
27
- @seen_new_data = false
28
31
  super(elem_name, bud_instance, collection_name, schema_in, &blk)
29
32
  end
30
33
 
@@ -34,19 +37,25 @@ module Bud
34
37
  @input_cache << item
35
38
  end
36
39
 
37
- @seen_new_data = true
38
40
  key = item.values_at(*@keys)
39
41
  group_state = @groups[key]
40
42
  if group_state.nil?
41
43
  @groups[key] = @aggpairs.map do |ap|
42
- input_val = ap[1].nil? ? item : item[ap[1]]
43
- ap[0].init(input_val)
44
+ if ap[1].nil?
45
+ ap[0].init(item)
46
+ else
47
+ ap[0].init(*item.values_at(*ap[1]))
48
+ end
44
49
  end
45
50
  else
46
51
  @aggpairs.each_with_index do |ap, agg_ix|
47
- input_val = ap[1].nil? ? item : item[ap[1]]
48
- state_val = ap[0].trans(group_state[agg_ix], input_val)[0]
49
- group_state[agg_ix] = state_val
52
+ state_val = group_state[agg_ix]
53
+ if ap[1].nil?
54
+ trans_rv = ap[0].trans(state_val, item)
55
+ else
56
+ trans_rv = ap[0].trans(state_val, *item.values_at(*ap[1]))
57
+ end
58
+ group_state[agg_ix] = trans_rv[0]
50
59
  end
51
60
  end
52
61
  end
@@ -62,14 +71,12 @@ module Bud
62
71
  puts "#{self.class}/#{self.tabname} invalidated" if $BUD_DEBUG
63
72
  @groups.clear
64
73
  @input_cache.clear if @elim_dups
65
- @seen_new_data = false
66
74
  end
67
75
 
68
76
  def flush
69
- # If we haven't seen any input since the last call to flush(), we're done:
70
- # our output would be the same as before.
71
- return unless @seen_new_data
72
- @seen_new_data = false
77
+ # Don't emit fresh output unless a rescan is needed
78
+ return unless @rescan
79
+ @rescan = false
73
80
 
74
81
  @groups.each do |key, group_state|
75
82
  rv = key.clone
@@ -87,7 +94,6 @@ module Bud
87
94
  raise Bud::Error, "multiple aggpairs #{aggpairs_in.map{|a| a.class.name}} in ArgAgg; only one allowed"
88
95
  end
89
96
  super(elem_name, bud_instance, collection_name, keys_in, aggpairs_in, schema_in, &blk)
90
- @agg, @aggcol = @aggpairs[0]
91
97
  @winners = {}
92
98
  end
93
99
 
@@ -101,18 +107,16 @@ module Bud
101
107
  key = @keys.map{|k| item[k]}
102
108
  group_state = @groups[key]
103
109
  if group_state.nil?
104
- @seen_new_data = true
105
110
  @groups[key] = @aggpairs.map do |ap|
106
111
  @winners[key] = [item]
107
- input_val = item[ap[1]]
108
- ap[0].init(input_val)
112
+ input_vals = item.values_at(*ap[1])
113
+ ap[0].init(*input_vals)
109
114
  end
110
115
  else
111
116
  @aggpairs.each_with_index do |ap, agg_ix|
112
- input_val = item[ap[1]]
113
- state_val, flag, *rest = ap[0].trans(group_state[agg_ix], input_val)
117
+ input_vals = item.values_at(*ap[1])
118
+ state_val, flag, *rest = ap[0].trans(group_state[agg_ix], *input_vals)
114
119
  group_state[agg_ix] = state_val
115
- @seen_new_data = true unless flag == :ignore
116
120
 
117
121
  case flag
118
122
  when :ignore
@@ -133,14 +137,13 @@ module Bud
133
137
  end
134
138
 
135
139
  def flush
136
- # If we haven't seen any input since the last call to flush(), we're done:
137
- # our output would be the same as before.
138
- return unless @seen_new_data
139
- @seen_new_data = false
140
+ # Don't emit fresh output unless a rescan is needed
141
+ return unless @rescan
142
+ @rescan = false
140
143
 
141
144
  @groups.each_key do |g|
142
145
  @winners[g].each do |t|
143
- push_out(t, false)
146
+ push_out(t)
144
147
  end
145
148
  end
146
149
  end
@@ -1,5 +1,4 @@
1
1
  require 'bud/executor/elements'
2
- require 'set'
3
2
 
4
3
  $EMPTY = []
5
4
  module Bud
@@ -58,12 +57,6 @@ module Bud
58
57
  super(@tabname, @bud_instance, nil, @cols)
59
58
  end
60
59
 
61
- public
62
- def copy_on_write
63
- @refcount -= 1
64
- return Bud::PushSHJoin.new(@all_rels_below, @bud_instance, [])
65
- end
66
-
67
60
  public
68
61
  def state_id # :nodoc: all
69
62
  object_id
@@ -366,10 +359,6 @@ module Bud
366
359
  # :col1 => :col2 (same as lefttable.col1 => righttable.col2)
367
360
  public
368
361
  def pairs(*preds, &blk)
369
- ## XXX Need to do this for all the join modifiers
370
- unless @refcount == 1
371
- return self.copy_on_write.pairs(preds, blk)
372
- end
373
362
  @origpreds = preds
374
363
  setup_preds(preds) unless preds.empty?
375
364
  # given new preds, the state for the join will be different. set it up again.
data/lib/bud/graphs.rb CHANGED
@@ -75,7 +75,7 @@ class GraphGen #:nodoc: all
75
75
  # its name is "CYC" + concat(sort(predicate names))
76
76
  depends.each do |d|
77
77
  # b/c bud_obj was pruned before serialization...
78
- (bud_obj, rule_id, lhs, op, body, nm) = d.to_a
78
+ bud_obj, rule_id, lhs, op, body, nm, in_body = d.to_a
79
79
  head = lhs
80
80
  body = body
81
81
 
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'bud'
3
+ require 'graphviz'
4
+
5
+ # A simple interface between graphviz and bud
6
+ module BudGraph
7
+ state do
8
+ interface input, :bnode, [:name] => [:meta]
9
+ interface input, :bedge, [:from, :to, :meta]
10
+ end
11
+ end
12
+
13
+ module BloomGraph
14
+ include BudGraph
15
+
16
+ state do
17
+ table :nodes, bnode.schema
18
+ table :edges, bedge.schema
19
+ end
20
+
21
+ bloom do
22
+ nodes <= bnode
23
+ edges <= bedge
24
+ end
25
+
26
+ def finish(ignore, name, fmt=:pdf)
27
+ it = ignore.to_set
28
+ tick
29
+ nodes.to_a.each do |n|
30
+ unless it.include? n.name.to_sym
31
+ @graph.add_nodes(n.name, n.meta)
32
+ end
33
+ end
34
+
35
+ edges.to_a.each do |e|
36
+ unless it.include? e.from.to_sym or it.include? e.to.to_sym
37
+ @graph.add_edges(e.from, e.to, e.meta)
38
+ end
39
+ end
40
+ @graph.output(fmt => name)
41
+ end
42
+
43
+ def initialize(opts={:type => :digraph})
44
+ @graph = GraphViz.new(:G, opts)
45
+ super
46
+ end
47
+ end