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  }