redshift 1.3.15 → 1.3.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,113 @@
1
+ # Records values of variables over time.
2
+ #
3
+ # A Tracer manages a two-tier system typical of simulations: Object and
4
+ # Variable. The Object tier is really a hash on arbitrary keys. The keys can be
5
+ # simulation objects, or they can be strings, symbols, etc.
6
+ #
7
+ # The values of this hash are the the Variable tier. More precisely, each value
8
+ # is another hash mapping variable name to Tracer::Var instances.
9
+ #
10
+ # The Tracer::Var class provides the primary per-variable functionality, and it
11
+ # can be used without the Tracer class. For example, it could be attched
12
+ # directly to the simulation object itself.
13
+ #
14
+ # The two-tier structure permits add/remove operations at the Object tier, which
15
+ # is convenient when objects enter or leave a simulation. Keeping the Tracer
16
+ # tiers separate from simulation objects makes it easier to run those objects
17
+ # without tracing, or to change the tracing strategy.
18
+ #
19
+ class Tracer
20
+ require 'redshift/util/tracer/var'
21
+
22
+ attr_reader :var_map, :default_opts
23
+
24
+ # Construct a Tracer which passes the +default_opts+ on to each
25
+ # var, overridden by the opts passed to #add.
26
+ def initialize default_opts = {}
27
+ @var_map = {}
28
+ @default_opts = default_opts
29
+ end
30
+
31
+ # Adds an item to the list of things to be recorded during #run!.
32
+ #
33
+ # If no block is given, the +key+ must be an object which has an
34
+ # attribute +name+.
35
+ #
36
+ # If a block is given, then it is called during #run! to determine
37
+ # the value that is stored in the Tracer. The +key+ and +name+ can be
38
+ # anything and are significant only for looking up traces in the Tracer.
39
+ # If the block accepts an argument, the associated +Var+ object is passed.
40
+ #
41
+ def add key, name, opts={}, &block
42
+ @var_map[key] ||= {}
43
+ var = Var.new(key, name, default_opts.merge(opts), &block)
44
+ @var_map[key][name] = var
45
+ end
46
+
47
+ # If +name+ is given, remove the trace of that variable
48
+ # associated with +key+. If name is not given, remove all traces
49
+ # associated with +key+. The removed Tracer::Var or hash of such objects
50
+ # is returned.
51
+ def remove key, name = nil
52
+ if name
53
+ h = @var_map[key]
54
+ r = h.delete name
55
+ @var_map.delete key if h.empty?
56
+ r
57
+ else
58
+ @var_map.delete key
59
+ end
60
+ end
61
+
62
+ # Called during a sumulation to record traces. In RedShift, typically called
63
+ # in the evolve {..} block, or in action clauses of transitions, or in
64
+ # hook methods.
65
+ def run!
66
+ @var_map.each do |key, h|
67
+ h.each do |name, var|
68
+ var.run!
69
+ end
70
+ end
71
+ end
72
+
73
+ # Convenience method to get a Var or a hash of Vars. Returns +nil+ if not
74
+ # found.
75
+ def [](key, name = nil)
76
+ if name
77
+ h = @var_map[key]
78
+ h && h[name]
79
+ else
80
+ @var_map[key]
81
+ end
82
+ end
83
+
84
+ class MissingVariableError < StandardError; end
85
+ class MissingKeyError < StandardError; end
86
+
87
+ # Yield once for each var associated with the +key+, or just once if
88
+ # +name+ given.
89
+ def each_var(key, name = nil)
90
+ if name
91
+ var = self[key, name] or
92
+ raise MissingVariableError, "No such var, #{key}.#{name}"
93
+ yield var
94
+ else
95
+ h = @var_map[key] or
96
+ raise MissingKeyError, "No such key, #{key}"
97
+ h.each do |name, var|
98
+ yield var
99
+ end
100
+ end
101
+ end
102
+
103
+ # Make a variable (or all vars of a key) inactive. No data will be traced
104
+ # unti #start is called.
105
+ def stop(key, name = nil)
106
+ each_var(key, name) {|var| var.active = false}
107
+ end
108
+
109
+ # Make a variable (or all vars of a key) active.
110
+ def start(key, name = nil)
111
+ each_var(key, name) {|var| var.active = true}
112
+ end
113
+ end
@@ -0,0 +1,145 @@
1
+ class Tracer
2
+ begin
3
+ require 'narray'
4
+
5
+ rescue LoadError
6
+ warn "Can't find narray lib;" +
7
+ " using Array instead of NVector for trace storage."
8
+ class Trace < Array
9
+ def initialize(*); super(); end
10
+ end
11
+
12
+ else
13
+ # A Trace is a linear store. Usually, the type of the stored data is
14
+ # homogenous and numeric. It is optimized for appending new data at the
15
+ # end of the store. Insertions are not efficient.
16
+ #
17
+ # If we have NArray, the we can use it for more compact storage than an
18
+ # array of ruby objects. The Trace class manages a list of fixed-size
19
+ # NVectors. Otherwise, if NArray is not available, Trace is just a ruby
20
+ # array.
21
+ class Trace
22
+ include Enumerable
23
+
24
+ DEFAULT_TYPE = "float"
25
+
26
+ DEFAULT_CHUNK_SIZE = 128
27
+
28
+ # The +opts+ is a hash of the form:
29
+ #
30
+ # { :type => t, :dims => nil or [d1,d2,...], :chunk_size => s }
31
+ #
32
+ # Strings may be used instead of symbols as the keys.
33
+ #
34
+ # The +type+ is passed to NVector.new. The +chunk_size+ is the size of
35
+ # each NVector. The +dims+ is the dimensions of each entry; default is
36
+ # +nil+, which means scalar entries, as does an empty array.
37
+ def initialize opts = {}
38
+ @type = (opts[:type] || opts["type"] || DEFAULT_TYPE).to_s
39
+ @chunk_size = opts[:chunk_size] || opts["chunk_size"] ||
40
+ DEFAULT_CHUNK_SIZE
41
+ @dims = opts[:dims] || opts["dims"] || []
42
+ @index_template = @dims.map {|d| true}
43
+ @index_template << nil
44
+ @nvector_new_args = [@type, *@dims] << @chunk_size
45
+ clear
46
+ end
47
+
48
+ # Clear the trace. Does not affect any state of the Var, such as
49
+ # the period counter.
50
+ def clear
51
+ @chunk_list = []
52
+ @index = @chunk_size # index of next insertion
53
+ end
54
+
55
+ # Iterate over values. (Note that this iteration yields the complete
56
+ # data structure for a point in time, whereas NVector#each iterates over
57
+ # individual numbers.)
58
+ def each
59
+ last = @chunk_list.last
60
+ it = @index_template.dup
61
+ @chunk_list.each do |chunk|
62
+ if chunk == last and @index < @chunk_size
63
+ @chunk_size.times do |i|
64
+ break if i == @index
65
+ it[-1] = i
66
+ yield chunk[*it]
67
+ end
68
+ else
69
+ @chunk_size.times do |i|
70
+ it[-1] = i
71
+ yield chunk[*it]
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ def size
78
+ (@chunk_list.size - 1) * @chunk_size + @index
79
+ end
80
+
81
+ alias length size
82
+
83
+ def << item
84
+ if @index == @chunk_size
85
+ @chunk_list << NVector.new(*@nvector_new_args)
86
+ @index = 0
87
+ end
88
+ @index_template[-1] = @index
89
+ @chunk_list.last[*@index_template] = item
90
+ @index += 1
91
+ self
92
+ end
93
+
94
+ alias push <<
95
+
96
+ # This is an inefficient, but sometimes convenient, implementation
97
+ # of #[]. For efficiency, use the Enumerable methods, if possible, or
98
+ # use #to_vector. This implementation does support negative indices.
99
+ def [](i)
100
+ if i.abs >= size
101
+ raise IndexError, "index out of range"
102
+ end
103
+
104
+ i %= size
105
+ it = @index_template.dup
106
+ it[-1] = i % @chunk_size
107
+ @chunk_list[i / @chunk_size][*it]
108
+ end
109
+
110
+ # This is the most efficient way to manipulate the data if Enumerable
111
+ # methods are not enough. It returns a copy of the trace data as a
112
+ # NVector, which supports a complete set of indexed collection methods and
113
+ # algebraic operations, including slicing, sorting, reshaping, statistics,
114
+ # and scalar and vector math. See NArray docs for details.
115
+ # Note that #[] works differently on NArray than the one defined above..
116
+ def to_vector
117
+ nva = @nvector_new_args.dup
118
+ nva[-1] = size
119
+ v = NVector.new(*nva)
120
+ last = @chunk_list.last
121
+ cs = @chunk_size
122
+ it = @index_template.dup
123
+ @chunk_list.each_with_index do |chunk, ci|
124
+ base = ci * @chunk_size
125
+ if chunk == last and @index < @chunk_size
126
+ it[-1] = base...base+@index
127
+ v[*it] = chunk[true, 0...@index]
128
+ else
129
+ it[-1] = base...base+cs
130
+ v[*it] = chunk
131
+ end
132
+ end
133
+ v
134
+ end
135
+
136
+ def inspect
137
+ to_vector.inspect
138
+ end
139
+
140
+ def to_s
141
+ entries.to_s
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,112 @@
1
+ require 'sci/tracer/trace'
2
+
3
+ class Tracer
4
+ # Represents the various options and controls associated with a variable
5
+ # being traced. The #trace attr holds the actual data. The Var object
6
+ # references the original object, and will therefore keep it from being GC-ed.
7
+ # If you want to retain the data without the object, keep only the #trace.
8
+ class Var
9
+ # Type affects precision and storage size. Can be any of the following
10
+ # strings, which are the same as the NArray scalar types:
11
+ #
12
+ # "byte" :: 1 byte unsigned integer
13
+ # "sint" :: 2 byte signed integer
14
+ # "int" :: 4 byte signed integer
15
+ # "sfloat" :: single precision float
16
+ # "float" :: double precision float
17
+ #
18
+ attr_reader :type
19
+
20
+ # Chunk size is the size of the chunks (in values, not bytes) in which
21
+ # memory is allocated. Each chunk is a +Vector+; the list of chunks is
22
+ # a ruby array. This only needs to be adjusted for performance tuning.
23
+ attr_reader :chunk_size
24
+
25
+ # Dimensions of each entry. A vlaue of +nil+ means scalar entries, as
26
+ # does an empty array. A value of [d1, d2...] indicates multidimensional
27
+ # data.
28
+ attr_reader :dims
29
+
30
+ # Is this var currently recording? See Tracer#stop and Tracer#start.
31
+ attr_accessor :active
32
+
33
+ # How many calls to #run! before trace is updated? A period of 0, 1, or
34
+ # nil means trace is updated on each call.
35
+ attr_accessor :period
36
+
37
+ # Counter to check for period expiration.
38
+ attr_accessor :counter
39
+
40
+ # Optional code to compute value, rather than use +name+. During #run!, the
41
+ # code will be called; if it accepts an arg, it will be passed the Var, from
42
+ # which #key and #name can be read. If code returns nil/false, no data is
43
+ # added to the trace.
44
+ attr_accessor :value_getter
45
+
46
+ # The +key+ can be either an object that has a named variable (accessed by
47
+ # the #name attr) or some arbitrary object (in which case a value_getter
48
+ # must be provided to compute the value).
49
+ attr_reader :key
50
+
51
+ # The name of the variable to access in the object.
52
+ attr_reader :name
53
+
54
+ # Stores the trace as a list of Vectors
55
+ attr_reader :trace
56
+
57
+ DEFAULT_TYPE = "float"
58
+
59
+ DEFAULT_CHUNK_SIZE = 128
60
+
61
+ DEFAULT_PERIOD = nil
62
+
63
+ # The +opts+ is a hash of the form:
64
+ #
65
+ # { :type => t, :chunk_size => s,:dims => nil or [d1,d2,...],
66
+ # :period => p }
67
+ #
68
+ # Strings may be used instead of symbols as the keys.
69
+ #
70
+ def initialize key, name, opts={}, &value_getter
71
+ @key, @name, @value_getter = key, name, value_getter
72
+
73
+ @type = (opts[:type] || opts["type"] || DEFAULT_TYPE).to_s
74
+ @chunk_size = opts[:chunk_size] || opts["chunk_size"] ||
75
+ DEFAULT_CHUNK_SIZE
76
+ @dims = opts[:dims] || opts["dims"] || []
77
+ @period = opts[:period] || opts["period"] || DEFAULT_PERIOD
78
+
79
+ @period = nil unless @period.kind_of?(Integer) and @period > 1
80
+ @counter = 0
81
+ @active = true
82
+ @trace = Trace.new(
83
+ :type => type, :chunk_size => chunk_size, :dims => dims)
84
+ end
85
+
86
+ def run!
87
+ return unless @active
88
+
89
+ if @period
90
+ @counter += 1
91
+ return if @counter < @period
92
+ @counter = 0
93
+ end
94
+
95
+ result =
96
+ if (vg=@value_getter)
97
+ case vg.arity
98
+ when 0; vg.call
99
+ when 1,-1; vg.call(self)
100
+ else raise ArgumentError,
101
+ "value_getter for #{@key}.#{@name} must take 0 or 1 arguments"
102
+ end
103
+ else
104
+ @key.send @name
105
+ end
106
+
107
+ if result
108
+ @trace << result
109
+ end
110
+ end
111
+ end
112
+ end
data/rakefile CHANGED
@@ -25,10 +25,10 @@ END
25
25
 
26
26
  history_file 'RELEASE-NOTES'
27
27
  changes File.read(history_file)[/^\w.*?(?=^\w)/m]
28
- (rdoc.dir File.readlink(rdoc.dir)) rescue nil
28
+ rdoc.dir "rdoc" ## Note: doc dir has original content files
29
29
 
30
30
  spec.opts << '--color'
31
- test.files = Dir["test/test-*.rb"]
31
+ test.files = Dir["test/test_*.rb"]
32
32
  }
33
33
 
34
34
  task :release => ["rubyforge:release", "rubyforge:doc_release"]
@@ -1,24 +1,25 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'redshift'
4
- require 'sci/random'
5
- require 'isaac'
4
+ require 'redshift/util/random'
5
+ ### TODO: add pure ruby ISAAC to util
6
+ #require 'isaac'
6
7
 
7
- # Adaptor class to use ISAAC with sci/random distributions.
8
- class ISAACGenerator < ISAAC
9
- def initialize(*seeds)
10
- super()
11
- if seeds.compact.empty?
12
- seeds = [Random::Sequence.random_seed]
13
- end
14
- @seeds = seeds
15
- srand(seeds)
16
- end
17
-
18
- attr_reader :seeds
19
-
20
- alias next rand
21
- end
8
+ # Adaptor class to use ISAAC with redshift/util/random distributions.
9
+ #class ISAACGenerator < ISAAC
10
+ # def initialize(*seeds)
11
+ # super()
12
+ # if seeds.compact.empty?
13
+ # seeds = [Random::Sequence.random_seed]
14
+ # end
15
+ # @seeds = seeds
16
+ # srand(seeds)
17
+ # end
18
+ #
19
+ # attr_reader :seeds
20
+ #
21
+ # alias next rand
22
+ #end
22
23
 
23
24
  include RedShift
24
25
 
@@ -69,13 +70,14 @@ class Flow_Transition < FlowTestComponent
69
70
  setup do
70
71
  self.x = 0
71
72
  @alarm_time = 0
72
- @alarm_seq = Random::Exponential.new \
73
- :generator => ISAACGenerator,
74
- :seed => nil, # 614822716,
73
+ @alarm_seq = Random::Exponential.new(
74
+ #:generator => ISAACGenerator,
75
+ :seed => 614822716,
75
76
  :mean => 0.5
76
- @state_seq = Random::Discrete.new \
77
- :generator => ISAACGenerator,
78
- :seed => nil, # 3871653669,
77
+ )
78
+ @state_seq = Random::Discrete.new(
79
+ #:generator => ISAACGenerator,
80
+ :seed => 3871653669, ## doesn't make sense to re-seed the same global gen
79
81
  :distrib =>
80
82
  {
81
83
  Alg => 100,
@@ -83,9 +85,10 @@ class Flow_Transition < FlowTestComponent
83
85
  Euler => 100,
84
86
  Empty => 100
85
87
  }
88
+ )
86
89
  puts "\n\n Flow_Transition used the following seeds:"
87
- puts " alarm seed = #{@alarm_seq.generator.seeds}"
88
- puts " state seed = #{@state_seq.generator.seeds}"
90
+ puts " alarm seed = #{@alarm_seq.generator.seeds rescue @alarm_seq.generator.seed}"
91
+ puts " state seed = #{@state_seq.generator.seeds rescue @state_seq.generator.seed}"
89
92
  end
90
93
 
91
94
  transition Enter => Switch,