Skip to content

Commit e814f1f

Browse files
committed
added error propogation for malformed inputs in energy stat calc
1 parent ee5e23d commit e814f1f

File tree

2 files changed

+77
-25
lines changed

2 files changed

+77
-25
lines changed

internal/cmd/perfcomp/energystatistics_test.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestEnergyStatistics(t *testing.T) {
3434

3535
t.Run("similar distributions should have small e,t,h values ", func(t *testing.T) {
3636
x, y := createTestVectors(1, 100, 1, 1, 105, 1)
37-
e, tstat, h := getEnergyStatistics(x, y)
37+
e, tstat, h, _ := getEnergyStatistics(x, y)
3838

3939
del := 1e-3
4040
// Limit precision of comparison to 3 digits after the decimal.
@@ -45,7 +45,7 @@ func TestEnergyStatistics(t *testing.T) {
4545

4646
t.Run("different distributions should have large e,t,h values", func(t *testing.T) {
4747
x, y := createTestVectors(1, 100, 1, 10000, 13000, 14)
48-
e, tstat, h := getEnergyStatistics(x, y)
48+
e, tstat, h, _ := getEnergyStatistics(x, y)
4949
del := 1e-3
5050

5151
assert.InDelta(t, 21859.691, e, del)
@@ -55,7 +55,7 @@ func TestEnergyStatistics(t *testing.T) {
5555

5656
t.Run("uni-variate distributions", func(t *testing.T) {
5757
x, y := createTestVectors(1, 300, 1, 1000, 5000, 10)
58-
e, tstat, h := getEnergyStatistics(x, y)
58+
e, tstat, h, _ := getEnergyStatistics(x, y)
5959
del := 1e-3
6060

6161
assert.InDelta(t, 4257.009, e, del)
@@ -67,11 +67,27 @@ func TestEnergyStatistics(t *testing.T) {
6767
x := mat.NewDense(10, 1, []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1})
6868
y := mat.NewDense(1, 1, []float64{1})
6969

70-
e, tstat, h := getEnergyStatistics(x, y)
70+
e, tstat, h, _ := getEnergyStatistics(x, y)
7171

7272
assert.Equal(t, 0.0, e)
7373
assert.Equal(t, 0.0, tstat)
7474
assert.Equal(t, 0.0, h)
7575
})
7676

77+
t.Run("energy stats returns errors on malformed input", func(t *testing.T) {
78+
x := mat.NewDense(2, 2, make([]float64, 4))
79+
y := mat.NewDense(2, 3, make([]float64, 6))
80+
81+
_, _, _, err := getEnergyStatistics(x, y)
82+
assert.NotEqual(t, nil, err)
83+
assert.ErrorContains(t, err, "both inputs must have the same number of columns")
84+
85+
x = mat.NewDense(2, 2, make([]float64, 4))
86+
y = mat.NewDense(3, 2, make([]float64, 6))
87+
88+
_, _, _, err = getEnergyStatistics(x, y)
89+
assert.NotEqual(t, nil, err)
90+
assert.ErrorContains(t, err, "both inputs must be column vectors")
91+
})
92+
7793
}

internal/cmd/perfcomp/main.go

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,18 @@ func getEnergyStatsForOneBenchmark(rd RawData, coll *mongo.Collection) ([]*Energ
234234
)
235235
}
236236

237-
pChange := getPercentageChange(patchVal[0], stableRegion.Mean)
238-
e, t, h := getEnergyStatistics(mat.NewDense(len(stableRegion.Values), 1, stableRegion.Values), mat.NewDense(1, 1, patchVal))
239-
z := getZScore(patchVal[0], stableRegion.Mean, stableRegion.Std)
237+
var z float64
238+
var pChange float64
239+
e, t, h, err := getEnergyStatistics(mat.NewDense(len(stableRegion.Values), 1, stableRegion.Values), mat.NewDense(1, 1, patchVal))
240+
if err != nil {
241+
log.Printf("Could not calculate energy stats for test %q, measurement %q: %v", testname, measurement, err)
242+
z = 0
243+
pChange = 0
244+
} else {
245+
z = getZScore(patchVal[0], stableRegion.Mean, stableRegion.Std)
246+
pChange = getPercentageChange(patchVal[0], stableRegion.Mean)
247+
248+
}
240249

241250
es := EnergyStats{
242251
Benchmark: testname,
@@ -294,64 +303,91 @@ func generatePRComment(energyStats []*EnergyStats, version string) string {
294303

295304
// Given two matrices, this function returns
296305
// (e, t, h) = (E-statistic, test statistic, e-coefficient of inhomogeneity)
297-
func getEnergyStatistics(x, y *mat.Dense) (float64, float64, float64) {
298-
n, _ := x.Dims()
299-
m, _ := y.Dims()
300-
nf := float64(n)
301-
mf := float64(m)
306+
func getEnergyStatistics(x, y *mat.Dense) (float64, float64, float64, error) {
307+
xrows, xcols := x.Dims()
308+
yrows, ycols := y.Dims()
309+
310+
if xcols != ycols {
311+
return 0, 0, 0, fmt.Errorf("both inputs must have the same number of columns")
312+
}
313+
314+
xrowsf := float64(xrows)
315+
yrowsf := float64(yrows)
302316

303317
var A float64 // E|X-Y|
304-
if nf > 0 && mf > 0 {
305-
A = getDistance(x, y) / (nf * mf)
318+
if xrowsf > 0 && yrowsf > 0 {
319+
dist, err := getDistance(x, y)
320+
if err != nil {
321+
return 0, 0, 0, err
322+
}
323+
A = dist / (xrowsf * yrowsf)
306324
} else {
307325
A = 0
308326
}
309327
var B float64 // E|X-X'|
310-
if nf > 0 {
311-
B = getDistance(x, x) / (nf * nf)
328+
if xrowsf > 0 {
329+
dist, err := getDistance(x, x)
330+
if err != nil {
331+
return 0, 0, 0, err
332+
}
333+
B = dist / (xrowsf * xrowsf)
312334
} else {
313335
B = 0
314336
}
315337
var C float64 // E|Y-Y'|
316-
if mf > 0 {
317-
C = getDistance(y, y) / (mf * mf)
338+
if yrowsf > 0 {
339+
dist, err := getDistance(y, y)
340+
if err != nil {
341+
return 0, 0, 0, err
342+
}
343+
C = dist / (yrowsf * yrowsf)
318344
} else {
319345
C = 0
320346
}
321347

322348
E := 2*A - B - C // D^2(F_x, F_y)
323-
T := ((nf * mf) / (nf + mf)) * E
349+
T := ((xrowsf * yrowsf) / (xrowsf + yrowsf)) * E
324350
var H float64
325351
if A > 0 {
326352
H = E / (2 * A)
327353
} else {
328354
H = 0
329355
}
330-
return E, T, H
356+
return E, T, H, nil
331357
}
332358

333359
// Given two vectors (expected 1 col),
334360
// this function returns the sum of distances between each pair.
335-
func getDistance(x, y *mat.Dense) float64 {
336-
xrows, _ := x.Dims()
337-
yrows, _ := y.Dims()
361+
func getDistance(x, y *mat.Dense) (float64, error) {
362+
xrows, xcols := x.Dims()
363+
yrows, ycols := y.Dims()
364+
365+
if xcols != 1 || ycols != 1 {
366+
return 0, fmt.Errorf("both inputs must be column vectors")
367+
}
338368

339369
var sum float64
340370

341371
for i := 0; i < xrows; i++ {
342372
for j := 0; j < yrows; j++ {
343-
sum += math.Sqrt(math.Pow((x.At(i, 0) - y.At(j, 0)), 2))
373+
sum += math.Abs(x.At(i, 0) - y.At(j, 0))
344374
}
345375
}
346-
return sum
376+
return sum, nil
347377
}
348378

349379
// Get Z score for result x, compared to mean u and st dev o.
350380
func getZScore(x, mu, sigma float64) float64 {
381+
if sigma == 0 {
382+
return math.NaN()
383+
}
351384
return (x - mu) / sigma
352385
}
353386

354387
// Get percentage change for result x compared to mean u.
355388
func getPercentageChange(x, mu float64) float64 {
389+
if mu == 0 {
390+
return math.NaN()
391+
}
356392
return ((x - mu) / mu) * 100
357393
}

0 commit comments

Comments
 (0)