kalibera 0.1
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.
- 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
|