go-hep.org/x/hep@v0.38.1/hbook/h2d_test.go (about) 1 // Copyright ©2016 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package hbook 6 7 import ( 8 "fmt" 9 "math" 10 "os" 11 "reflect" 12 "testing" 13 14 "github.com/google/go-cmp/cmp" 15 "gonum.org/v1/plot/plotter" 16 ) 17 18 func TestH2D(t *testing.T) { 19 const ( 20 nx = 10 21 xmin = 0.0 22 xmax = 100.0 23 ny = 10 24 ymin = 0.0 25 ymax = 100.0 26 ) 27 28 h := NewH2D(nx, xmin, xmax, ny, ymin, ymax) 29 if h == nil { 30 t.Fatalf("nil pointer to H2D") 31 } 32 33 if min := h.XMin(); min != xmin { 34 t.Errorf("x-min error: got=%v. want=%v\n", min, xmin) 35 } 36 if max := h.XMax(); max != xmax { 37 t.Errorf("x-max error: got=%v. want=%v\n", max, xmax) 38 } 39 if min := h.YMin(); min != ymin { 40 t.Errorf("y-min error: got=%v. want=%v\n", min, ymin) 41 } 42 if max := h.YMax(); max != ymax { 43 t.Errorf("y-max error: got=%v. want=%v\n", max, ymax) 44 } 45 46 if name := h.Name(); name != "" { 47 t.Errorf("name error: got=%q. want=%q\n", name, "") 48 } 49 h.Annotation()["name"] = "h1" 50 if name := h.Name(); name != "h1" { 51 t.Errorf("name error: got=%q. want=%q\n", name, "h1") 52 } 53 54 if n := h.Entries(); n != 0 { 55 t.Errorf("entries error: got=%v. want=%v\n", n, 0) 56 } 57 58 h.Fill(1, 1, 1) 59 if n, want := h.Entries(), int64(1); n != want { 60 t.Errorf("entries error: got=%v. want=%v\n", n, want) 61 } 62 if n, want := h.EffEntries(), 1.0; n != want { 63 t.Errorf("eff-entries error: got=%v. want=%v\n", n, want) 64 } 65 66 if w, want := h.SumW(), 1.0; w != want { 67 t.Errorf("sum-w: got=%v. want=%v\n", w, want) 68 } 69 70 if w2, want := h.SumW2(), 1.0; w2 != want { 71 t.Errorf("sum-w2: got=%v. want=%v\n", w2, want) 72 } 73 74 if v, want := h.XMean(), 1.0; v != want { 75 t.Errorf("x-mean: got=%v. want=%v\n", v, want) 76 } 77 78 if v, want := h.XVariance(), math.NaN(); !math.IsNaN(v) { 79 t.Errorf("x-variance: got=%v. want=%v\n", v, want) 80 } 81 82 if v, want := h.XStdDev(), math.NaN(); !math.IsNaN(v) { 83 t.Errorf("x-std-dev: got=%v. want=%v\n", v, want) 84 } 85 86 if v, want := h.YMean(), 1.0; v != want { 87 t.Errorf("y-mean: got=%v. want=%v\n", v, want) 88 } 89 90 if v, want := h.YVariance(), math.NaN(); !math.IsNaN(v) { 91 t.Errorf("y-variance: got=%v. want=%v\n", v, want) 92 } 93 94 if v, want := h.YStdDev(), math.NaN(); !math.IsNaN(v) { 95 t.Errorf("y-std-dev: got=%v. want=%v\n", v, want) 96 } 97 98 h.Fill(23, 1, 1) 99 if n, want := h.Entries(), int64(2); n != want { 100 t.Errorf("entries error: got=%v. want=%v\n", n, want) 101 } 102 if n, want := h.EffEntries(), 2.0; n != want { 103 t.Errorf("eff-entries error: got=%v. want=%v\n", n, want) 104 } 105 if w, want := h.SumW(), 2.0; w != want { 106 t.Errorf("sum-w: got=%v. want=%v\n", w, want) 107 } 108 109 if w2, want := h.SumW2(), 2.0; w2 != want { 110 t.Errorf("sum-w2: got=%v. want=%v\n", w2, want) 111 } 112 113 if v, want := h.XMean(), 12.0; v != want { 114 t.Errorf("x-mean: got=%v. want=%v\n", v, want) 115 } 116 117 if v, want := h.XVariance(), 242.0; v != want { 118 t.Errorf("x-variance: got=%v. want=%v\n", v, want) 119 } 120 121 if v, want := h.XStdDev(), 15.556349186104045; v != want { 122 t.Errorf("x-std-dev: got=%v. want=%v\n", v, want) 123 } 124 125 if v, want := h.YMean(), 1.0; v != want { 126 t.Errorf("y-mean: got=%v. want=%v\n", v, want) 127 } 128 129 if v, want := h.YVariance(), 0.0; v != want { 130 t.Errorf("y-variance: got=%v. want=%v\n", v, want) 131 } 132 133 if v, want := h.YStdDev(), 0.0; v != want { 134 t.Errorf("y-std-dev: got=%v. want=%v\n", v, want) 135 } 136 137 h.Fill(200, 200, 1) 138 if w, want := h.SumW(), 3.0; w != want { 139 t.Errorf("sum-w: got=%v. want=%v\n", w, want) 140 } 141 142 if w2, want := h.SumW2(), 3.0; w2 != want { 143 t.Errorf("sum-w2: got=%v. want=%v\n", w2, want) 144 } 145 146 if v, want := h.XMean(), 74.66666666666667; v != want { 147 t.Errorf("x-mean: got=%v. want=%v\n", v, want) 148 } 149 150 if v, want := h.XVariance(), 11902.333333333334; v != want { 151 t.Errorf("x-variance: got=%v. want=%v\n", v, want) 152 } 153 154 if v, want := h.XStdDev(), 109.09781543795152; v != want { 155 t.Errorf("x-std-dev: got=%v. want=%v\n", v, want) 156 } 157 158 if v, want := h.YMean(), 67.33333333333333; v != want { 159 t.Errorf("y-mean: got=%v. want=%v\n", v, want) 160 } 161 162 if v, want := h.YVariance(), 13200.333333333334; v != want { 163 t.Errorf("y-variance: got=%v. want=%v\n", v, want) 164 } 165 166 if v, want := h.YStdDev(), 114.89270356873553; v != want { 167 t.Errorf("y-std-dev: got=%v. want=%v\n", v, want) 168 } 169 170 h.Fill(-100, -100, 0.5) 171 if n, want := h.Entries(), int64(4); n != want { 172 t.Errorf("entries error: got=%v. want=%v\n", n, want) 173 } 174 if n, want := h.EffEntries(), 3.769230769230769; n != want { 175 t.Errorf("eff-entries error: got=%v. want=%v\n", n, want) 176 } 177 if w, want := h.SumW(), 3.5; w != want { 178 t.Errorf("sum-w: got=%v. want=%v\n", w, want) 179 } 180 181 if w2, want := h.SumW2(), 3.25; w2 != want { 182 t.Errorf("sum-w2: got=%v. want=%v\n", w2, want) 183 } 184 185 if v, want := h.XMean(), 49.714285714285715; v != want { 186 t.Errorf("x-mean: got=%v. want=%v\n", v, want) 187 } 188 189 if v, want := h.XVariance(), 14342.111111111111; v != want { 190 t.Errorf("x-variance: got=%v. want=%v\n", v, want) 191 } 192 193 if v, want := h.XStdDev(), 119.75855339436558; v != want { 194 t.Errorf("x-std-dev: got=%v. want=%v\n", v, want) 195 } 196 197 if v, want := h.YMean(), 43.42857142857143; v != want { 198 t.Errorf("y-mean: got=%v. want=%v\n", v, want) 199 } 200 201 if v, want := h.YVariance(), 14933.666666666666; v != want { 202 t.Errorf("y-variance: got=%v. want=%v\n", v, want) 203 } 204 205 if v, want := h.YStdDev(), 122.20338238635895; v != want { 206 t.Errorf("y-std-dev: got=%v. want=%v\n", v, want) 207 } 208 } 209 210 func TestH2Edges(t *testing.T) { 211 h := NewH2DFromEdges( 212 []float64{+0, +1, +2, +3}, 213 []float64{-3, -2, -1, +0}, 214 ) 215 if got, want := h.XMin(), +0.0; got != want { 216 t.Errorf("got xmin=%v. want=%v", got, want) 217 } 218 if got, want := h.YMin(), -3.0; got != want { 219 t.Errorf("got ymin=%v. want=%v", got, want) 220 } 221 if got, want := h.XMax(), +3.0; got != want { 222 t.Errorf("got xmax=%v. want=%v", got, want) 223 } 224 if got, want := h.YMax(), +0.0; got != want { 225 t.Errorf("got ymax=%v. want=%v", got, want) 226 } 227 } 228 229 func TestH2EdgesWithPanics(t *testing.T) { 230 for _, test := range []struct { 231 xs []float64 232 ys []float64 233 }{ 234 { 235 xs: []float64{0}, 236 ys: []float64{0, 1}, 237 }, 238 { 239 xs: []float64{0}, 240 ys: []float64{0}, 241 }, 242 { 243 xs: []float64{0, 1, 0.5, 2}, 244 ys: []float64{0, 1, 2}, 245 }, 246 { 247 xs: []float64{0, 1, 1}, 248 ys: []float64{0, 1, 2}, 249 }, 250 { 251 xs: []float64{0, 1, 0, 1}, 252 ys: []float64{0, 1, 2}, 253 }, 254 { 255 xs: []float64{0, 1, 2, 2}, 256 ys: []float64{0, 1, 2}, 257 }, 258 { 259 xs: []float64{0, 1, 2, 2, 2}, 260 ys: []float64{0, 1, 2}, 261 }, 262 } { 263 { 264 panicked, _ := panics(func() { 265 _ = NewH2DFromEdges(test.xs, test.ys) 266 }) 267 if !panicked { 268 t.Errorf("edges {x=%v, y=%v} should have panicked", test.xs, test.ys) 269 } 270 } 271 { 272 panicked, _ := panics(func() { 273 _ = NewH2DFromEdges(test.ys, test.xs) 274 }) 275 if !panicked { 276 t.Errorf("edges {y=%v, x=%v} should have panicked", test.xs, test.ys) 277 } 278 } 279 } 280 } 281 282 // check H2D can be plotted 283 var _ plotter.GridXYZ = ((*H2D)(nil)).GridXYZ() 284 285 func TestH2DWriteYODA(t *testing.T) { 286 h := NewH2D(5, -1, 1, 5, -2, +2) 287 h.Fill(+0.5, +1, 1) 288 h.Fill(-0.5, +1, 1) 289 h.Fill(+0.0, -1, 1) 290 291 chk, err := h.MarshalYODA() 292 if err != nil { 293 t.Fatal(err) 294 } 295 296 ref, err := os.ReadFile("testdata/h2d_v2_golden.yoda") 297 if err != nil { 298 t.Fatal(err) 299 } 300 301 if !reflect.DeepEqual(chk, ref) { 302 t.Fatalf("h2d file differ:\n%s\n", 303 cmp.Diff( 304 string(ref), 305 string(chk), 306 ), 307 ) 308 } 309 } 310 311 func TestH2DReadYODAv1(t *testing.T) { 312 ref, err := os.ReadFile("testdata/h2d_v1_golden.yoda") 313 if err != nil { 314 t.Fatal(err) 315 } 316 317 var h H2D 318 err = h.UnmarshalYODA(ref) 319 if err != nil { 320 t.Fatal(err) 321 } 322 323 chk, err := h.marshalYODAv1() 324 if err != nil { 325 t.Fatal(err) 326 } 327 328 if !reflect.DeepEqual(chk, ref) { 329 t.Fatalf("h2d file differ:\n%s\n", 330 cmp.Diff( 331 string(ref), 332 string(chk), 333 ), 334 ) 335 } 336 } 337 338 func TestH2DReadYODAv2(t *testing.T) { 339 ref, err := os.ReadFile("testdata/h2d_v2_golden.yoda") 340 if err != nil { 341 t.Fatal(err) 342 } 343 344 var h H2D 345 err = h.UnmarshalYODA(ref) 346 if err != nil { 347 t.Fatal(err) 348 } 349 350 chk, err := h.MarshalYODA() 351 if err != nil { 352 t.Fatal(err) 353 } 354 355 if !reflect.DeepEqual(chk, ref) { 356 t.Fatalf("h2d file differ:\n%s\n", 357 cmp.Diff( 358 string(ref), 359 string(chk), 360 ), 361 ) 362 } 363 } 364 365 func TestH2DBin(t *testing.T) { 366 h := NewH2DFromEdges( 367 []float64{+0, +1, +2, +3}, 368 []float64{-3, -2, -1, +0}, 369 ) 370 if got, want := h.XMin(), +0.0; got != want { 371 t.Errorf("got xmin=%v. want=%v", got, want) 372 } 373 if got, want := h.YMin(), -3.0; got != want { 374 t.Errorf("got ymin=%v. want=%v", got, want) 375 } 376 if got, want := h.XMax(), +3.0; got != want { 377 t.Errorf("got xmax=%v. want=%v", got, want) 378 } 379 if got, want := h.YMax(), +0.0; got != want { 380 t.Errorf("got ymax=%v. want=%v", got, want) 381 } 382 383 h.Fill(0, -3, 1) 384 385 h.Fill(0, -2, 1) 386 h.Fill(0, -2, 1) 387 388 h.Fill(1, -2, 1) 389 h.Fill(1, -2, 1) 390 h.Fill(1, -2, 1) 391 392 for _, tc := range []struct { 393 x, y float64 394 bin int 395 }{ 396 {0, -3, 1}, 397 {0, -2, 2}, 398 {1, -2, 3}, 399 {-1, -10, -1}, 400 {0, -10, -1}, 401 } { 402 t.Run(fmt.Sprintf("x,y=(%v,%v)", tc.x, tc.y), func(t *testing.T) { 403 bin := h.Bin(tc.x, tc.y) 404 if tc.bin < 0 && bin == nil { 405 // ok 406 return 407 } 408 if bin == nil { 409 t.Fatalf("unexpected nil bin") 410 } 411 412 if bin.EffEntries() != float64(tc.bin) { 413 t.Fatalf("x=%v,%v got=%v %v, want=%d", tc.x, tc.y, bin.EffEntries(), bin.Entries(), tc.bin) 414 } 415 }) 416 } 417 } 418 419 func TestH2DFillN(t *testing.T) { 420 h1 := NewH2D(10, 0, 10, 10, 0, 10) 421 h2 := NewH2D(10, 0, 10, 10, 0, 10) 422 423 xs := []float64{1, 2, 3, 4} 424 ys := []float64{1, 2, 3, 4} 425 ws := []float64{1, 2, 1, 1} 426 427 for i := range xs { 428 h1.Fill(xs[i], ys[i], ws[i]) 429 } 430 h2.FillN(xs, ys, ws) 431 432 if s1, s2 := h1.SumW(), h2.SumW(); s1 != s2 { 433 t.Fatalf("invalid sumw: h1=%v, h2=%v", s1, s2) 434 } 435 436 for i := range xs { 437 h1.Fill(xs[i], ys[i], 1) 438 } 439 h2.FillN(xs, ys, nil) 440 441 if s1, s2 := h1.SumW(), h2.SumW(); s1 != s2 { 442 t.Fatalf("invalid sumw: h1=%v, h2=%v", s1, s2) 443 } 444 445 func() { 446 defer func() { 447 err := recover() 448 if err == nil { 449 t.Fatalf("expected a panic!") 450 } 451 const want = "hbook: lengths mismatch" 452 if got, want := err.(error).Error(), want; got != want { 453 t.Fatalf("invalid panic message:\ngot= %q\nwant=%q", got, want) 454 } 455 }() 456 457 h2.FillN(xs, ys[:len(xs)-2], nil) 458 }() 459 460 func() { 461 defer func() { 462 err := recover() 463 if err == nil { 464 t.Fatalf("expected a panic!") 465 } 466 const want = "hbook: lengths mismatch" 467 if got, want := err.(error).Error(), want; got != want { 468 t.Fatalf("invalid panic message:\ngot= %q\nwant=%q", got, want) 469 } 470 }() 471 472 h2.FillN(xs, []float64{1}, ws) 473 }() 474 475 func() { 476 defer func() { 477 err := recover() 478 if err == nil { 479 t.Fatalf("expected a panic!") 480 } 481 const want = "hbook: lengths mismatch" 482 if got, want := err.(error).Error(), want; got != want { 483 t.Fatalf("invalid panic message:\ngot= %q\nwant=%q", got, want) 484 } 485 }() 486 487 h2.FillN(xs, ys, []float64{1}) 488 }() 489 }