redshift 1.3.15 → 1.3.16

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.
@@ -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,