kalibera 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +12 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +9 -0
- data/kalibera.gemspec +24 -0
- data/lib/kalibera.rb +1 -0
- data/lib/kalibera/data.rb +369 -0
- data/test/test_data.rb +410 -0
- metadata +115 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7716023defd20bb3f94d5c7f2f26959dc92ec502
|
4
|
+
data.tar.gz: 86a9b2b33cf855415087f6a0b9c807442447a205
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cc782c6b650f3aaa9ea72752fa7cc1cf0ad3d9181f93733568716cd380400564311f231f1f63e6ac3e7093f36358b048d55c1929f399b3bb72f7f38323712dd2
|
7
|
+
data.tar.gz: 9c122a46376f5af2d83d4ec97de5a4388c1713ed89608bc5359fbe19b5f6caf670ea84872e6e741d9ac70fc2ed40a5f48973e427369aef9ff56c18b4d2edbffd
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (C) King's College London, created by Edd Barrett and Carl
|
2
|
+
Friedrich Bolz
|
3
|
+
|
4
|
+
Ruby transliteration (C) Chris Seaton 2014
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
data/kalibera.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
metadata = JSON.parse(IO.read(File.expand_path('../../shared_metadata.json', __FILE__)))
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "kalibera"
|
7
|
+
spec.version = metadata["metadata"]["version"]
|
8
|
+
spec.authors = ["Edd Barrett", "Carl Friedrich Bolz", "Chris Seaton"]
|
9
|
+
spec.email = ["chris@chrisseaton.com"]
|
10
|
+
spec.summary = metadata["metadata"]["short_descr"]
|
11
|
+
spec.description = metadata["metadata"]["long_descr"]
|
12
|
+
spec.homepage = metadata["metadata"]["url"]
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.test_files = spec.files.grep(%r{^test/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
20
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
21
|
+
|
22
|
+
spec.add_dependency "rbzip2", "~> 0.2.0"
|
23
|
+
spec.add_dependency "memoist", "~> 0.11.0"
|
24
|
+
end
|
data/lib/kalibera.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "kalibera/data"
|
@@ -0,0 +1,369 @@
|
|
1
|
+
require "stringio"
|
2
|
+
require "base64"
|
3
|
+
require "rbzip2"
|
4
|
+
require "bigdecimal"
|
5
|
+
require "memoist"
|
6
|
+
|
7
|
+
module Kalibera
|
8
|
+
|
9
|
+
CONSTANTS = RBzip2::Decompressor.new(StringIO.new(Base64.decode64("""\
|
10
|
+
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
|
52
|
+
NLhVQA=="""))).read.split().map { |x| Float(x) }
|
53
|
+
|
54
|
+
# Look up the 95% quantile from constant table.
|
55
|
+
def self.student_t_quantile95(ndeg)
|
56
|
+
index = ndeg - 1
|
57
|
+
if index >= CONSTANTS.size
|
58
|
+
index = -1 # the quantile converges, we just take the last value
|
59
|
+
end
|
60
|
+
CONSTANTS[index]
|
61
|
+
end
|
62
|
+
|
63
|
+
ConfRange = Struct.new(:lower, :median, :upper) do
|
64
|
+
def error
|
65
|
+
Kalibera.mean([upper - median, median - lower])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns a tuples (lower, median, upper), where:
|
70
|
+
# lower: lower bound of 95% confidence interval
|
71
|
+
# median: the median value of the data
|
72
|
+
# upper: upper bound of 95% confidence interval
|
73
|
+
#
|
74
|
+
# Arguments:
|
75
|
+
# means -- the list of means (need not be sorted).
|
76
|
+
def self.confidence_slice(means, confidence="0.95")
|
77
|
+
means = means.sort
|
78
|
+
# There may be >1 median indicies, i.e. data is even-sized.
|
79
|
+
lower, middle_indicies, upper = confidence_slice_indicies(means.size, confidence)
|
80
|
+
median = mean(middle_indicies.map { |i| means[i] })
|
81
|
+
ConfRange.new(means[lower], median, means[upper - 1]) # upper is *exclusive*
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns a triple (lower, mean_indicies, upper) so that l[lower:upper]
|
85
|
+
# gives confidence_level of all samples. Mean_indicies is a tuple of one or
|
86
|
+
# two indicies that correspond to the mean position
|
87
|
+
#
|
88
|
+
# Keyword arguments:
|
89
|
+
# confidence_level -- desired level of confidence as a Decimal instance.
|
90
|
+
def self.confidence_slice_indicies(length, confidence_level=BigDecimal.new('0.95'))
|
91
|
+
raise unless !confidence_level.instance_of?(Float)
|
92
|
+
confidence_level = BigDecimal.new(confidence_level)
|
93
|
+
raise unless confidence_level.instance_of?(BigDecimal)
|
94
|
+
exclude = (1 - confidence_level) / 2
|
95
|
+
|
96
|
+
if length % 2 == 0
|
97
|
+
mean_indicies = [length / 2 - 1, length / 2] # TRANSLITERATION: was //
|
98
|
+
else
|
99
|
+
mean_indicies = [length / 2] # TRANSLITERATION: was //
|
100
|
+
end
|
101
|
+
|
102
|
+
lower_index = Integer(
|
103
|
+
(exclude * length).round(0, BigDecimal::ROUND_DOWN) # TRANSLITERATION: was quantize 1.
|
104
|
+
)
|
105
|
+
|
106
|
+
upper_index = Integer(
|
107
|
+
((1 - exclude) * length).round(0, BigDecimal::ROUND_UP) # TRANSLITERATION: was quantize 1.
|
108
|
+
)
|
109
|
+
|
110
|
+
[lower_index, mean_indicies, upper_index]
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.mean(l)
|
114
|
+
l.inject(0, :+) / Float(l.size)
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.geomean(l)
|
118
|
+
l.inject(1, :*) ** (1.0 / Float(l.size))
|
119
|
+
end
|
120
|
+
|
121
|
+
class Data
|
122
|
+
|
123
|
+
extend Memoist
|
124
|
+
|
125
|
+
# Instances of this class store measurements (corresponding to
|
126
|
+
# the Y_... in the papers).
|
127
|
+
#
|
128
|
+
# Arguments:
|
129
|
+
# data -- Dict mapping tuples of all but the last index to lists of values.
|
130
|
+
# reps -- List of reps for each level, high to low.
|
131
|
+
def initialize(data, reps)
|
132
|
+
@data = data
|
133
|
+
@reps = reps
|
134
|
+
|
135
|
+
# check that all data is there
|
136
|
+
|
137
|
+
array = reps.map { |i| (0...i).to_a }
|
138
|
+
array[0].product(*array.drop(1)).each do |index|
|
139
|
+
self[*index] # does not crash
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def [](*indicies)
|
144
|
+
raise unless indicies.size == @reps.size
|
145
|
+
x = @data[indicies[0...indicies.size-1]]
|
146
|
+
raise unless !x.nil?
|
147
|
+
x[indicies[-1]]
|
148
|
+
end
|
149
|
+
|
150
|
+
# Computes a list of all possible data indcies gievn that
|
151
|
+
# start <= index <= stop are fixed.
|
152
|
+
def index_iterator(start=0, stop=nil)
|
153
|
+
if stop.nil?
|
154
|
+
stop = n
|
155
|
+
end
|
156
|
+
|
157
|
+
maximum_indicies = @reps[start...stop]
|
158
|
+
remaining_indicies = maximum_indicies.map { |maximum| (0...maximum).to_a }
|
159
|
+
return [[]] if remaining_indicies.empty?
|
160
|
+
remaining_indicies[0].product(*remaining_indicies.drop(1))
|
161
|
+
end
|
162
|
+
|
163
|
+
# The number of levels in the experiment.
|
164
|
+
def n
|
165
|
+
@reps.size
|
166
|
+
end
|
167
|
+
|
168
|
+
# The number of repetitions for level i.
|
169
|
+
#
|
170
|
+
# Arguments:
|
171
|
+
# i -- mathematical index.
|
172
|
+
def r(i)
|
173
|
+
raise unless 1 <= i
|
174
|
+
raise unless i <= n
|
175
|
+
index = n - i
|
176
|
+
@reps[index]
|
177
|
+
end
|
178
|
+
|
179
|
+
# Compute the mean across a number of values.
|
180
|
+
#
|
181
|
+
# Keyword arguments:
|
182
|
+
# indicies -- tuple of fixed indicies over which to compute the mean,
|
183
|
+
# given from left to right. The remaining indicies are variable.
|
184
|
+
def mean(indicies=[])
|
185
|
+
remaining_indicies_cross_product =
|
186
|
+
index_iterator(start=indicies.size)
|
187
|
+
alldata = remaining_indicies_cross_product.map { |remaining| self[*(indicies + remaining)] }
|
188
|
+
Kalibera.mean(alldata)
|
189
|
+
end
|
190
|
+
|
191
|
+
memoize :mean
|
192
|
+
|
193
|
+
# Biased estimator S_i^2.
|
194
|
+
#
|
195
|
+
# Arguments:
|
196
|
+
# i -- the mathematical index of the level from which to compute S_i^2
|
197
|
+
def Si2(i)
|
198
|
+
raise unless 1 <= i
|
199
|
+
raise unless i <= n
|
200
|
+
# @reps is indexed from the left to right
|
201
|
+
index = n - i
|
202
|
+
factor = 1.0
|
203
|
+
|
204
|
+
# We compute this iteratively leveraging the fact that
|
205
|
+
# 1 / (a * b) = (1 / a) / b
|
206
|
+
for rep in @reps[0, index]
|
207
|
+
factor /= rep
|
208
|
+
end
|
209
|
+
# Then at this point we have:
|
210
|
+
# factor * (1 / (r_i - 1)) = factor / (r_i - 1)
|
211
|
+
factor /= @reps[index] - 1
|
212
|
+
|
213
|
+
# Second line of the above definition, the lines are multiplied.
|
214
|
+
indicies = index_iterator(0, index+1)
|
215
|
+
sum = 0.0
|
216
|
+
for index in indicies
|
217
|
+
a = mean(index)
|
218
|
+
b = mean(index[0,index.size-1])
|
219
|
+
sum += (a - b) ** 2
|
220
|
+
end
|
221
|
+
factor * sum
|
222
|
+
end
|
223
|
+
|
224
|
+
memoize :Si2
|
225
|
+
|
226
|
+
# Compute the unbiased T_i^2 variance estimator.
|
227
|
+
#
|
228
|
+
# Arguments:
|
229
|
+
# i -- the mathematical index from which to compute T_i^2.
|
230
|
+
def Ti2(i)
|
231
|
+
# This is the broken implementation of T_i^2 shown in the pubslished
|
232
|
+
# version of "Rigorous benchmarking in reasonable time". Tomas has
|
233
|
+
# since fixed this in local versions of the paper.
|
234
|
+
#@memoize
|
235
|
+
#def broken_Ti2(self, i)
|
236
|
+
# """ Compute the unbiased T_i^2 variance estimator.
|
237
|
+
#
|
238
|
+
# Arguments:
|
239
|
+
# i -- the mathematical index from which to compute T_i^2.
|
240
|
+
# """
|
241
|
+
#
|
242
|
+
# raise unless 1 <= i <= n
|
243
|
+
# if i == 1:
|
244
|
+
# return self.Si2(1)
|
245
|
+
# return self.Si2(i) - self.Ti2(i - 1) / self.r(i - 1)
|
246
|
+
|
247
|
+
# This is the correct definition of T_i^2
|
248
|
+
|
249
|
+
raise unless 1 <= i
|
250
|
+
raise unless i <= n
|
251
|
+
if i == 1
|
252
|
+
return Si2(1)
|
253
|
+
end
|
254
|
+
Si2(i) - Si2(i - 1) / r(i - 1)
|
255
|
+
end
|
256
|
+
|
257
|
+
memoize :Ti2
|
258
|
+
|
259
|
+
# Computes the optimal number of repetitions for a given level.
|
260
|
+
#
|
261
|
+
# Note that the resulting number of reps is not rounded.
|
262
|
+
#
|
263
|
+
# Arguments:
|
264
|
+
# i -- the mathematical level of which to compute optimal reps.
|
265
|
+
# costs -- A list of costs for each level, *high* to *low*.
|
266
|
+
def optimalreps(i, costs)
|
267
|
+
# NOTE: Does not round
|
268
|
+
costs = costs.map { |x| Float(x) }
|
269
|
+
raise unless 1 <= i
|
270
|
+
raise unless i < n
|
271
|
+
index = n - i
|
272
|
+
return (costs[index - 1] / costs[index] *
|
273
|
+
Ti2(i) / Ti2(i + 1)) ** 0.5
|
274
|
+
end
|
275
|
+
|
276
|
+
memoize :optimalreps
|
277
|
+
|
278
|
+
# Compute the 95% confidence interval.
|
279
|
+
def confidence95
|
280
|
+
degfreedom = @reps[0] - 1
|
281
|
+
student_t_quantile95(degfreedom) *
|
282
|
+
(Si2(n) / @reps[0]) ** 0.5
|
283
|
+
end
|
284
|
+
|
285
|
+
# Compute a list of simulated means from bootstrap resampling.
|
286
|
+
#
|
287
|
+
# Note that, resampling occurs with replacement.
|
288
|
+
#
|
289
|
+
# Keyword arguments:
|
290
|
+
# iterations -- Number of resamples (and thus means) generated.
|
291
|
+
def bootstrap_means(iterations=1000)
|
292
|
+
means = []
|
293
|
+
for i in 0...iterations
|
294
|
+
values = bootstrap_sample()
|
295
|
+
means.push(Kalibera.mean(values))
|
296
|
+
end
|
297
|
+
means.sort()
|
298
|
+
means
|
299
|
+
end
|
300
|
+
|
301
|
+
# Compute a confidence interval via bootstrap method.
|
302
|
+
#
|
303
|
+
# Keyword arguments:
|
304
|
+
# iterations -- Number of resamplings to base result upon. Default is 10000.
|
305
|
+
# confidence -- The required confidence. Default is "0.95" (95%).
|
306
|
+
def bootstrap_confidence_interval(iterations=10000, confidence="0.95")
|
307
|
+
means = bootstrap_means(iterations)
|
308
|
+
Kalibera.confidence_slice(means, confidence)
|
309
|
+
end
|
310
|
+
|
311
|
+
def random_measurement_sample(index=[])
|
312
|
+
results = []
|
313
|
+
if index.size == n
|
314
|
+
results.push self[*index]
|
315
|
+
else
|
316
|
+
indicies = (0...@reps[index.size]).map { |i| rand(@reps[index.size]) }
|
317
|
+
for single_index in indicies
|
318
|
+
newindex = index + [single_index]
|
319
|
+
for value in random_measurement_sample(newindex)
|
320
|
+
results.push value
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
results
|
325
|
+
end
|
326
|
+
|
327
|
+
def bootstrap_sample
|
328
|
+
random_measurement_sample
|
329
|
+
end
|
330
|
+
|
331
|
+
def bootstrap_quotient(other, iterations=10000, confidence='0.95')
|
332
|
+
ratios = []
|
333
|
+
for _ in 0...iterations
|
334
|
+
ra = bootstrap_sample()
|
335
|
+
rb = other.bootstrap_sample()
|
336
|
+
mean_ra = Kalibera.mean(ra)
|
337
|
+
mean_rb = Kalibera.mean(rb)
|
338
|
+
|
339
|
+
if mean_rb == 0 # protect against divide by zero
|
340
|
+
ratios.push(Float::INFINITY)
|
341
|
+
else
|
342
|
+
ratios.push(mean_ra / mean_rb)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
ratios.sort!
|
346
|
+
Kalibera.confidence_slice(ratios, confidence).values
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
def self.bootstrap_geomean(l_data_a, l_data_b, iterations=10000, confidence='0.95')
|
352
|
+
raise "lists need to match" unless l_data_a.size == l_data_b.size
|
353
|
+
geomeans = []
|
354
|
+
iterations.times do
|
355
|
+
ratios = []
|
356
|
+
l_data_a.zip(l_data_b).each do |a, b|
|
357
|
+
ra = a.bootstrap_sample
|
358
|
+
rb = b.bootstrap_sample
|
359
|
+
mean_ra = mean(ra)
|
360
|
+
mean_rb = mean(rb)
|
361
|
+
ratios << mean_ra / mean_rb
|
362
|
+
end
|
363
|
+
geomeans << geomean(ratios)
|
364
|
+
end
|
365
|
+
geomeans.sort!
|
366
|
+
confidence_slice(geomeans, confidence)
|
367
|
+
end
|
368
|
+
|
369
|
+
end
|
data/test/test_data.rb
ADDED
@@ -0,0 +1,410 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
|
3
|
+
require "kalibera"
|
4
|
+
|
5
|
+
# We need to match Python's random numbers when testing
|
6
|
+
|
7
|
+
class TestData < Kalibera::Data
|
8
|
+
|
9
|
+
RAND = [0, 2, 2, 0, 1, 1, 1, 2, 0, 0, 2, 1, 2, 0, 1, 2, 0, 2, 2, 0, 0, 1,
|
10
|
+
2, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 2, 2, 0, 0]
|
11
|
+
|
12
|
+
def initialize(data, reps)
|
13
|
+
super
|
14
|
+
@rand_counter = 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def reset_local_rand
|
18
|
+
@rand_counter = 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def rand(r)
|
22
|
+
raise "mock rand designed for range=3" unless r == 3
|
23
|
+
raise "mock rand out of data" unless @rand_counter < RAND.size
|
24
|
+
|
25
|
+
n = RAND[@rand_counter]
|
26
|
+
@rand_counter += 1
|
27
|
+
n
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
class TestKaliberaData < Test::Unit::TestCase
|
33
|
+
|
34
|
+
def test_indicies
|
35
|
+
d = TestData.new({
|
36
|
+
[0, 0] => [1, 2, 3, 4, 5],
|
37
|
+
[0, 1] => [3, 4, 5, 6, 7]
|
38
|
+
}, [1, 2, 5])
|
39
|
+
|
40
|
+
assert_equal 1, d[0, 0, 0]
|
41
|
+
assert_equal 5, d[0, 0, 4]
|
42
|
+
assert_equal 5, d[0, 1, 2]
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_rep_levels
|
46
|
+
d = TestData.new({
|
47
|
+
[0, 0] => [1, 2, 3, 4, 5],
|
48
|
+
[0, 1] => [3, 4, 5, 6, 7]
|
49
|
+
}, [1, 2, 5])
|
50
|
+
|
51
|
+
assert_equal 5, d.r(1) # lowest level, i.e. arity of the lists in the map
|
52
|
+
assert_equal 2, d.r(2)
|
53
|
+
assert_equal 1, d.r(3)
|
54
|
+
|
55
|
+
# indexs are one based, so 0 or less is invalid
|
56
|
+
assert_raise RuntimeError do
|
57
|
+
d.r(0)
|
58
|
+
end
|
59
|
+
|
60
|
+
assert_raise RuntimeError do
|
61
|
+
d.r(-1337)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Since we have 3 levels here, levels 4 and above are bogus
|
65
|
+
assert_raise RuntimeError do
|
66
|
+
d.r(4)
|
67
|
+
end
|
68
|
+
|
69
|
+
assert_raise RuntimeError do
|
70
|
+
d.r(666)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_index_iter
|
75
|
+
d = TestData.new({
|
76
|
+
[0, 0] => [1, 2, 3, 4, 5],
|
77
|
+
[0, 1] => [3, 4, 5, 6, 7]
|
78
|
+
}, [1, 2, 5])
|
79
|
+
|
80
|
+
assert_equal [
|
81
|
+
[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4],
|
82
|
+
[0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 1, 3], [0, 1, 4],
|
83
|
+
], d.index_iterator()
|
84
|
+
assert_equal [
|
85
|
+
[0, 0], [0, 1], [0, 2], [0, 3], [0, 4],
|
86
|
+
[1, 0], [1, 1], [1, 2], [1, 3], [1, 4],
|
87
|
+
], d.index_iterator(start=1)
|
88
|
+
assert_equal [[0]], d.index_iterator(start=0, stop=1)
|
89
|
+
assert_equal [[0], [1]], d.index_iterator(start=1, stop=2)
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_index_means
|
93
|
+
d = TestData.new({
|
94
|
+
[0, 0] => [0, 2]
|
95
|
+
}, [1, 1, 2])
|
96
|
+
|
97
|
+
assert_equal 1, d.mean([])
|
98
|
+
assert_equal 1, d.mean([0, 0])
|
99
|
+
assert_equal d[0, 0, 0], d.mean([0, 0, 0])
|
100
|
+
assert_equal d[0, 0, 1], d.mean([0, 0, 1])
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_index_means2
|
104
|
+
# Suppose we have three levels, so n = 3.
|
105
|
+
# For the sake of example, level 1 is repetitions, level 2 is executions,
|
106
|
+
# and level 3 is compilations. Now suppose we repeat level 3 twice,
|
107
|
+
# level 2 twice and level 3 five times.
|
108
|
+
#
|
109
|
+
# This would be a valid data set:
|
110
|
+
# Note that the indicies are off-by-one due to python indicies starting
|
111
|
+
# from 0.
|
112
|
+
d = TestData.new({ [0, 0] => [ 3, 4, 4, 1, 2 ], # times for compile 1, execution 1
|
113
|
+
[0, 1] => [ 3, 3, 3, 3, 3 ], # compile 1, execution 2
|
114
|
+
[1, 0] => [ 1, 2, 3, 4, 5 ], # compile 2, execution 1
|
115
|
+
[1, 1] => [ 1, 1, 4, 4, 1 ], # compile 2, execution 2
|
116
|
+
}, [2, 2, 5]) # counts for each level (highest to lowest)
|
117
|
+
|
118
|
+
# By calling mean with an empty tuple we compute the mean at all levels
|
119
|
+
# i.e. the mean of all times:
|
120
|
+
x = [3, 4, 4, 1, 2, 3, 3, 3, 3, 3, 1, 2, 3, 4, 5, 1, 1, 4, 4, 1]
|
121
|
+
expect = x.inject(0, :+)/Float(x.size)
|
122
|
+
assert_equal d.mean([]), expect
|
123
|
+
|
124
|
+
# By calling with a singleton tuple we compute the mean for a given
|
125
|
+
#compilation. E.g. compilation 2
|
126
|
+
x = [1, 2, 3, 4, 5, 1, 1, 4, 4, 1]
|
127
|
+
expect = x.inject(0, :+) / Float(x.size)
|
128
|
+
assert_equal d.mean([1]), expect
|
129
|
+
|
130
|
+
# By calling with a pair we compute the mean for a given compile
|
131
|
+
# and execution combo.
|
132
|
+
# E.g. compile 1, execution 2, which is obviously a mean of 3.
|
133
|
+
assert_equal 3, d.mean([0, 1])
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_si2
|
137
|
+
d = TestData.new({
|
138
|
+
[0, 0] => [0, 0]
|
139
|
+
}, [1, 1, 2])
|
140
|
+
|
141
|
+
assert_equal 0, d.Si2(1)
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_si2_bigger_example
|
145
|
+
# Let's compute S_1^2 for the following data
|
146
|
+
d = TestData.new({
|
147
|
+
[0, 0] => [3,4,3],
|
148
|
+
[0, 1] => [1.2, 3.1, 3],
|
149
|
+
[1, 0] => [0.2, 1, 1.5],
|
150
|
+
[1, 1] => [1, 2, 3]
|
151
|
+
}, [2, 2, 3])
|
152
|
+
|
153
|
+
# So we have n = 3, r = (2, 2, 3)
|
154
|
+
# By my reckoning we should get something close to 0.72667 (working below)
|
155
|
+
# XXX Explanation from whiteboard need to go here XXX
|
156
|
+
|
157
|
+
assert_less_equal (d.Si2(1)-0.72667).abs, 0.0001
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_ti2
|
161
|
+
# To verify this, consider the following data:
|
162
|
+
d = TestData.new({
|
163
|
+
[0, 0] => [3,4,3],
|
164
|
+
[0, 1] => [1.2, 3.1, 3],
|
165
|
+
[1, 0] => [0.2, 1, 1.5],
|
166
|
+
[1, 1] => [1, 2, 3]
|
167
|
+
}, [2, 2, 3])
|
168
|
+
|
169
|
+
# Let's manually look at S_i^2 where 1 <= i <= n:
|
170
|
+
#si_vec = [ d.Si2(i) for i in range(1, 4) ]
|
171
|
+
#print(si_vec)
|
172
|
+
|
173
|
+
ti_vec = (1...4).map { |i| d.Ti2(i) }
|
174
|
+
expect = [ 0.7266667, 0.262777778, 0.7747 ]
|
175
|
+
|
176
|
+
(0...expect.size).each do |i|
|
177
|
+
assert (ti_vec[i] - expect[i]).abs <= 0.0001, "#{} <= 0.0001"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_optimal_reps
|
182
|
+
d = TestData.new({
|
183
|
+
[0, 0] => [3,4,3],
|
184
|
+
[0, 1] => [1.2, 3.1, 3],
|
185
|
+
[1, 0] => [0.2, 1, 1.5],
|
186
|
+
[1, 1] => [1, 2, 3]
|
187
|
+
}, [2, 2, 3])
|
188
|
+
|
189
|
+
#ti_vec = [ d.Ti2(i) for i in range (1, 4) ]
|
190
|
+
#print(ti_vec)
|
191
|
+
|
192
|
+
# And suppose the costs (high level to low) are 100, 20 and 3 (seconds)
|
193
|
+
# By my reckoning, the optimal repetition counts should be r_1 = 5, r_2 = 2
|
194
|
+
# XXX show working XXX
|
195
|
+
got = [1,2].map { |i|d.optimalreps(i, [100, 20, 3]) }
|
196
|
+
expect = [4.2937, 1.3023]
|
197
|
+
|
198
|
+
(0...got.size).each do |i|
|
199
|
+
assert_less_equal (got[i] - expect[i]).abs, 0.001
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_worked_example_3_level
|
204
|
+
# three level experiment
|
205
|
+
# This is the worked example from the paper.
|
206
|
+
data = TestData.new({
|
207
|
+
[0, 0] => [9.0, 5.0], [0, 1] => [8.0, 3.0],
|
208
|
+
[1, 0] => [10.0, 6.0], [1, 1] => [7.0, 11.0],
|
209
|
+
[2, 0] => [1.0, 12.0], [2, 1] => [2.0, 4.0],
|
210
|
+
}, [3, 2, 2])
|
211
|
+
|
212
|
+
correct = {
|
213
|
+
[0, 0] => 7.0,
|
214
|
+
[0, 1] => 5.5,
|
215
|
+
[1, 0] => 8.0,
|
216
|
+
[1, 1] => 9.0,
|
217
|
+
[2, 0] => 6.5,
|
218
|
+
[2, 1] => 3.0,
|
219
|
+
}
|
220
|
+
|
221
|
+
data.index_iterator(0, 2).each do |index|
|
222
|
+
assert data.mean(index) == correct[index]
|
223
|
+
end
|
224
|
+
|
225
|
+
assert_equal 6.5, data.mean()
|
226
|
+
|
227
|
+
assert_equal 16.5, data.Si2(1).round(1)
|
228
|
+
assert_equal 2.6, data.Si2(2).round(1)
|
229
|
+
assert_equal 3.6, data.Si2(3).round(1)
|
230
|
+
assert_equal 16.5, data.Ti2(1).round(1)
|
231
|
+
assert_equal -5.7, data.Ti2(2).round(1)
|
232
|
+
assert_equal 2.3, data.Ti2(3).round(1)
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_worked_example_2_level
|
236
|
+
data = TestData.new({
|
237
|
+
[0] => [9.0, 5.0, 8.0, 3.0],
|
238
|
+
[1] => [10.0, 6.0, 7.0, 11.0],
|
239
|
+
[2] => [1.0, 12.0, 2.0, 4.0],
|
240
|
+
}, [3, 4])
|
241
|
+
|
242
|
+
correct = {[0] => 6.3,
|
243
|
+
[1] => 8.5,
|
244
|
+
[2] => 4.8,
|
245
|
+
}
|
246
|
+
data.index_iterator(0, 1).each do |index|
|
247
|
+
assert data.mean(index).round(1) == correct[index]
|
248
|
+
end
|
249
|
+
|
250
|
+
assert_equal 6.5, data.mean()
|
251
|
+
|
252
|
+
assert_equal 12.7, data.Si2(1).round(1)
|
253
|
+
assert_equal 3.6, data.Si2(2).round(1)
|
254
|
+
|
255
|
+
assert_equal 12.7, data.Ti2(1).round(1)
|
256
|
+
assert_equal 0.4, data.Ti2(2).round(1)
|
257
|
+
end
|
258
|
+
|
259
|
+
def test_bootstrap
|
260
|
+
# XXX needs info on how expected val was computed
|
261
|
+
data = TestData.new({
|
262
|
+
[0] => [ 2.5, 3.1, 2.7 ],
|
263
|
+
[1] => [ 5.1, 1.1, 2.3 ],
|
264
|
+
[2] => [ 4.7, 5.5, 7.1 ],
|
265
|
+
}, [3, 3])
|
266
|
+
data.reset_local_rand
|
267
|
+
|
268
|
+
expect = 4.8111111111
|
269
|
+
got = data.bootstrap_means(1) # one iteration
|
270
|
+
|
271
|
+
assert_less_equal (got[0] - expect).abs, 0.0001
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_confidence_slice_indicies
|
275
|
+
assert_equal [1, [4, 5], 9], Kalibera.confidence_slice_indicies(10, '0.8')
|
276
|
+
assert_equal [1, [5], 10], Kalibera.confidence_slice_indicies(11, '0.8')
|
277
|
+
assert_equal [25, [499, 500], 975], Kalibera.confidence_slice_indicies(1000)
|
278
|
+
end
|
279
|
+
|
280
|
+
def test_confidence_slice
|
281
|
+
# Suppose we get back the means:
|
282
|
+
means = (0...1000).map { |x| x + 15 } # already sorted
|
283
|
+
|
284
|
+
# For a data set of size 1000, we expect alpha/2 to be 25
|
285
|
+
# (for a 95% confidence interval)
|
286
|
+
alpha_over_two = means.size * 0.025
|
287
|
+
assert(alpha_over_two) == 25
|
288
|
+
|
289
|
+
# Therefore we lose 25 items off each end of the means list.
|
290
|
+
# The first 25 indicies are 0, ...0, 24, so lower bound should be index 25.
|
291
|
+
# The last 25 indicies are -1, ...0, -25, so upper bound is index -26
|
292
|
+
# Put differently, the last 25 indicies are 999, ...0, 975
|
293
|
+
|
294
|
+
lower_index = Integer(alpha_over_two.floor)
|
295
|
+
upper_index = Integer(-alpha_over_two.ceil - 1)
|
296
|
+
lobo, hibo = [means[lower_index], means[upper_index]]
|
297
|
+
|
298
|
+
# Since the data is the index plus 15, we should get an
|
299
|
+
# interval: [25+15, 974+15]
|
300
|
+
expect = [25+15, 974+15]
|
301
|
+
assert_equal expect, [lobo, hibo]
|
302
|
+
|
303
|
+
# There is strictly speaking no median of 1000 items.
|
304
|
+
# We take the mean of the two middle items items 500 and 501 at indicies
|
305
|
+
# 499 and 500. Since the data is the index + 15, the middle values are
|
306
|
+
# 514 and 515, the mean of which is 514.5
|
307
|
+
median = 514.5
|
308
|
+
|
309
|
+
# Check the implementation.
|
310
|
+
confrange = Kalibera.confidence_slice(means)
|
311
|
+
got_lobo, got_median, got_hibo = confrange.values
|
312
|
+
assert_equal got_lobo, confrange.lower
|
313
|
+
assert_equal got_median, confrange.median
|
314
|
+
assert_equal got_hibo, confrange.upper
|
315
|
+
|
316
|
+
assert_equal lobo, got_lobo
|
317
|
+
assert_equal hibo, got_hibo
|
318
|
+
assert_equal got_median, median
|
319
|
+
|
320
|
+
assert_equal Kalibera.mean([median - lobo, hibo - median]), confrange.error
|
321
|
+
end
|
322
|
+
|
323
|
+
def test_confidence_slice_pass_confidence_level
|
324
|
+
means = (0...10).map { |x| Float(x) }
|
325
|
+
low, mean, high = Kalibera.confidence_slice(means, '0.8').values
|
326
|
+
assert_equal (4 + 5) / 2.0, mean
|
327
|
+
assert_equal 1, low
|
328
|
+
assert_equal 8, high
|
329
|
+
|
330
|
+
|
331
|
+
means = (0...11).map { |x| Float(x) }
|
332
|
+
low, mean, high = Kalibera.confidence_slice(means, '0.8').values
|
333
|
+
assert_equal 5, mean
|
334
|
+
assert_equal 1, low
|
335
|
+
assert_equal 9, high
|
336
|
+
end
|
337
|
+
|
338
|
+
def test_confidence_quotient
|
339
|
+
data1 = TestData.new({
|
340
|
+
[0] => [ 2.5, 3.1, 2.7 ],
|
341
|
+
[1] => [ 5.1, 1.1, 2.3 ],
|
342
|
+
[2] => [ 4.7, 5.5, 7.1 ],
|
343
|
+
}, [3, 3])
|
344
|
+
data2 = TestData.new({
|
345
|
+
[0] => [ 3.5, 4.1, 3.7 ],
|
346
|
+
[1] => [ 6.1, 2.1, 3.3 ],
|
347
|
+
[2] => [ 5.7, 6.5, 8.1 ],
|
348
|
+
}, [3, 3])
|
349
|
+
|
350
|
+
data1.reset_local_rand
|
351
|
+
data2.reset_local_rand
|
352
|
+
a = data1.bootstrap_sample
|
353
|
+
b = data2.bootstrap_sample
|
354
|
+
|
355
|
+
data1.reset_local_rand
|
356
|
+
data2.reset_local_rand
|
357
|
+
_, mean, _ = data1.bootstrap_quotient(data2, iterations=1)
|
358
|
+
assert_equal Kalibera.mean(a) / Kalibera.mean(b), mean
|
359
|
+
end
|
360
|
+
|
361
|
+
def test_confidence_quotient_div_zero
|
362
|
+
data1 = TestData.new({
|
363
|
+
[0] => [ 2.5, 3.1, 2.7 ],
|
364
|
+
[1] => [ 5.1, 1.1, 2.3 ],
|
365
|
+
[2] => [ 4.7, 5.5, 7.1 ],
|
366
|
+
}, [3, 3])
|
367
|
+
data2 = TestData.new({ # This has a mean of zero
|
368
|
+
[0] => [ 0, 0, 0],
|
369
|
+
[1] => [ 0, 0, 0],
|
370
|
+
[2] => [ 0, 0, 0],
|
371
|
+
}, [3, 3])
|
372
|
+
|
373
|
+
# Since all ratios will be +inf, the median should also be +inf
|
374
|
+
_, median, _ = data1.bootstrap_quotient(data2, iterations=1)
|
375
|
+
assert_equal Float::INFINITY, median
|
376
|
+
end
|
377
|
+
|
378
|
+
def test_geomean
|
379
|
+
assert_equal 1, Kalibera.geomean([10, 0.1])
|
380
|
+
assert_equal 1, Kalibera.geomean([1])
|
381
|
+
end
|
382
|
+
|
383
|
+
# This requires a very large volume of random data, which we can't easily
|
384
|
+
# just store in the mock random generator above.
|
385
|
+
|
386
|
+
#def test_geomean_data
|
387
|
+
# data1 = TestData.new({
|
388
|
+
# [0] => [ 2.9, 3.1, 3.0 ],
|
389
|
+
# [1] => [ 3.1, 2.6, 3.3 ],
|
390
|
+
# [2] => [ 3.2, 3.0, 2.9 ],
|
391
|
+
# }, [3, 3])
|
392
|
+
# data2 = TestData.new({
|
393
|
+
# [0] => [ 3.9, 4.1, 4.0 ],
|
394
|
+
# [1] => [ 4.1, 3.6, 4.3 ],
|
395
|
+
# [2] => [ 4.2, 4.0, 3.9 ],
|
396
|
+
# }, [3, 3])
|
397
|
+
#
|
398
|
+
# _, mean1, _ = data1.bootstrap_quotient(data2)
|
399
|
+
# _, mean2, _ = Kalibera.bootstrap_geomean([data1], [data2])
|
400
|
+
# assert_equal mean2.round(3), mean1.round(3)
|
401
|
+
#
|
402
|
+
# (_, mean, _) = Kalibera.bootstrap_geomean([data1, data2], [data2, data1])
|
403
|
+
# assert_equal 1.0, mean.round(5)
|
404
|
+
#end
|
405
|
+
|
406
|
+
def assert_less_equal(x, y)
|
407
|
+
assert x <= y, "#{x.inspect} <= #{y.inspect}"
|
408
|
+
end
|
409
|
+
|
410
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kalibera
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Edd Barrett
|
8
|
+
- Carl Friedrich Bolz
|
9
|
+
- Chris Seaton
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2016-05-16 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bundler
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.7'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '1.7'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: rake
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '10.0'
|
36
|
+
type: :development
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '10.0'
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: rbzip2
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 0.2.0
|
50
|
+
type: :runtime
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.2.0
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: memoist
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 0.11.0
|
64
|
+
type: :runtime
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 0.11.0
|
71
|
+
description: 'libkalibera contains reimplementations of the statistical computations
|
72
|
+
for benchmarking evaluation from the following papers by Tomas Kalibera and Richard
|
73
|
+
Jones: ''Rigorous benchmarking in reasonable time''; ''Quantifying performance changes
|
74
|
+
with effect size confidence intervals''.'
|
75
|
+
email:
|
76
|
+
- chris@chrisseaton.com
|
77
|
+
executables: []
|
78
|
+
extensions: []
|
79
|
+
extra_rdoc_files: []
|
80
|
+
files:
|
81
|
+
- Gemfile
|
82
|
+
- Gemfile.lock
|
83
|
+
- LICENSE.txt
|
84
|
+
- Rakefile
|
85
|
+
- kalibera.gemspec
|
86
|
+
- lib/kalibera.rb
|
87
|
+
- lib/kalibera/data.rb
|
88
|
+
- test/test_data.rb
|
89
|
+
homepage: http://soft-dev.org/src/libkalibera/
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.5.1
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: An implementation of Tomas Kalibera's statistically rigorous benchmarking
|
113
|
+
method.
|
114
|
+
test_files:
|
115
|
+
- test/test_data.rb
|