bud 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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