gitee.com/zhongguo168a/gocodes@v0.0.0-20230609140523-e1828349603f/thirdpart/vector/vector_test.go (about)

     1  // Author: slowpoke <proxypoke at lavabit dot com>
     2  // Repository: https://gist.github.com/proxypoke/vector
     3  //
     4  // This program is free software under the non-terms
     5  // of the Anti-License. Do whatever the fuck you want.
     6  
     7  package vector
     8  
     9  import (
    10  	"math"
    11  	"math/rand"
    12  	"testing"
    13  )
    14  
    15  // ========================== [ Constructor Tests ] ===========================
    16  
    17  // Creates vectors with dimension from 0 to 99, checks if they actually have
    18  // that dimension, then checks if the values are correctly initialized to 0.
    19  func TestNew(t *testing.T) {
    20  	var i, j uint
    21  	for i = 0; i < 100; i++ {
    22  		v := New(i)
    23  		if v.Dim() != i {
    24  			t.Errorf("Wrong dimension. Got %d, expected %d.", v.Dim(), i)
    25  		}
    26  		for j = 0; j < i; j++ {
    27  			// XXX: If the Get method errors, this test will still pass. This
    28  			// is because Get() would then return an uninitialized float64 for
    29  			// val, which is 0 and therefore what the test expects.
    30  			val := v.Get(j)
    31  			if val != 0 {
    32  				t.Error("Newly initialized vector has a value != 0.")
    33  			}
    34  		}
    35  	}
    36  }
    37  
    38  // Creates vectors with randomized slices, then checks whether they have the
    39  // correct dimension (len(slice)) and whether they have been correctly
    40  // initialized.
    41  func TestNewFrom(t *testing.T) {
    42  	var i, j uint
    43  	for i = 0; i < 100; i++ {
    44  		randslice := makeRandSlice(i)
    45  		v := NewFrom(randslice)
    46  		if v.Dim() != i {
    47  			t.Errorf("Wrong dimension. Got %d, expected %d.", v.Dim(), i)
    48  		}
    49  		for j = 0; j < i; j++ {
    50  			val := v.Get(j)
    51  			if val != randslice[j] {
    52  				t.Error(
    53  					"Wrong values in vector initialized from a random slice.")
    54  			}
    55  		}
    56  	}
    57  }
    58  
    59  // Creates pseudo-random vectors with various dimensions, copies them and
    60  // verifies that the new vector is equal.
    61  func TestCopy(t *testing.T) {
    62  	var i uint
    63  	for i = 0; i < 100; i++ {
    64  		v := makeRandomVector(i)
    65  		w := v.Copy()
    66  		if !Equal(v, w) {
    67  			t.Error("Copied vector is not equal to source vector.")
    68  		}
    69  	}
    70  }
    71  
    72  // =================== [ General Methods/Functions Tests ] ====================
    73  
    74  // Creates pseudo-random vectors with various dimensions, then check if Get()
    75  // returns the correct values and errors on out-of-range indexes.
    76  func TestGet(t *testing.T) {
    77  	var i uint
    78  	for i = 0; i < 100; i++ {
    79  		v := makeRandomVector(i)
    80  		for j, val := range v.dims {
    81  			getval := v.Get(uint(j))
    82  			if getval != 0 {
    83  				t.Error("Get() errored on a correct index.")
    84  			}
    85  			if val != getval {
    86  				t.Error("Get() returned a wrong value.")
    87  			}
    88  		}
    89  	}
    90  }
    91  
    92  // Creates uninitialized vectors of various dimensions, then sets their values
    93  // to pseudo-random values. It then compares those values to check if they
    94  // were set correctly. Also verifies is SetDBObject() correctly errors on out-of-range
    95  // indexes.
    96  func TestSet(t *testing.T) {
    97  	var i, j uint
    98  	for i = 0; i < 100; i++ {
    99  		v := New(i)
   100  		for j = 0; j < i; j++ {
   101  			val := rand.ExpFloat64()
   102  			err := v.Set(j, val)
   103  			if err != nil {
   104  				t.Error("SetDBObject() errored on a correct index.")
   105  			}
   106  			if v.dims[j] != val {
   107  				t.Error("SetDBObject didn't correctly set a value.")
   108  			}
   109  		}
   110  		err := v.Set(v.Dim(), 0)
   111  		if err == nil {
   112  			t.Error("SetDBObject didn't error on an out-of-range index.")
   113  		}
   114  	}
   115  }
   116  
   117  // Creates a vector with known length, then compares the expected value with
   118  // what Len() returns.
   119  func TestLen(t *testing.T) {
   120  	v := New(1)
   121  	v.Set(0, 2) // has length 2
   122  	if v.Len() != 2 {
   123  		t.Error("Len returned a wrong length")
   124  	}
   125  }
   126  
   127  // Creates Vectors which are known to be (un)equal, then verifies that Equal()
   128  // has correct oytput.
   129  func TestEqual(t *testing.T) {
   130  	slc := make([]float64, 10)
   131  	for i := range slc {
   132  		slc[i] = float64(i)
   133  	}
   134  
   135  	v := NewFrom(slc)
   136  	w := NewFrom(slc)
   137  	if !Equal(v, w) {
   138  		t.Error("Equal() != true for equal vectors.")
   139  	}
   140  
   141  	w = New(10)
   142  	if Equal(v, w) {
   143  		t.Error("Equal() == true for unequal vectors.")
   144  	}
   145  }
   146  
   147  // =========================== [ Opt Tests ] ============================
   148  
   149  // Creates pesudo-random vectors, then adds them first as a non-destructive,
   150  // then as an in-place operations, checking if both operation were correct.
   151  func TestAdd(t *testing.T) {
   152  	var i, j uint
   153  	for i = 1; i < 100; i++ {
   154  		a := makeRandomVector(i)
   155  		b := makeRandomVector(i)
   156  		c, _ := Add(a, b)
   157  
   158  		for j = 0; j < i; j++ {
   159  			if c.dims[j] != a.dims[j]+b.dims[j] {
   160  				t.Error("Addition failed, didn't get expected values.")
   161  				t.Logf("%f + %f != %f", a.dims[j], b.dims[j], c.dims[j])
   162  			}
   163  		}
   164  
   165  		// Test in-place addition.
   166  		c = a.Copy()
   167  		c.Add(b)
   168  
   169  		for j = 0; j < i; j++ {
   170  			if c.dims[j] != a.dims[j]+b.dims[j] {
   171  				t.Error(
   172  					"In-place Addition failed, didn't get expected values.")
   173  				t.Logf("%f + %f != %f", a.dims[j], b.dims[j], c.dims[j])
   174  			}
   175  		}
   176  	}
   177  }
   178  
   179  // Same as TestAdd, but with substraction. Heck, it's basically the same code.
   180  func TestSubstract(t *testing.T) {
   181  	var i, j uint
   182  	for i = 1; i < 100; i++ {
   183  		a := makeRandomVector(i)
   184  		b := makeRandomVector(i)
   185  		c, _ := Substract(a, b)
   186  
   187  		for j = 0; j < i; j++ {
   188  			if c.dims[j] != a.dims[j]-b.dims[j] {
   189  				t.Error("Substraction failed, didn't get expected values.")
   190  				t.Logf("%f - %f != %f", a.dims[j], b.dims[j], c.dims[j])
   191  			}
   192  		}
   193  
   194  		// Test in-place sybstraction
   195  		c = a.Copy()
   196  		c.Substract(b)
   197  
   198  		for j = 0; j < i; j++ {
   199  			if c.dims[j] != a.dims[j]-b.dims[j] {
   200  				t.Error(
   201  					"In-place Substraction failed, didn't get expected values.")
   202  				t.Logf("%f - %f != %f", a.dims[j], b.dims[j], c.dims[j])
   203  			}
   204  		}
   205  	}
   206  }
   207  
   208  // Creates pseudo-random vectors, does scalar multiplication with pseudo-random
   209  // floats, then checks if the result is correct. It checks both the in-place
   210  // and the non-destructive version.
   211  func TestScale(t *testing.T) {
   212  	var i, j uint
   213  	for i = 0; i < 100; i++ {
   214  		a := makeRandomVector(i)
   215  		x := rand.ExpFloat64()
   216  		b := Scale(a, x)
   217  
   218  		for j = 0; j < i; j++ {
   219  			if b.dims[j] != a.dims[j]*x {
   220  				t.Error("Scalar Multiplication failed, ",
   221  					"didn't get expected values.")
   222  				t.Logf("%f * %f != %f", a.dims[j], x, b.dims[j])
   223  			}
   224  		}
   225  
   226  		// Test in-place scalar multiplication
   227  		b = a.Copy()
   228  		b.Scale(x)
   229  
   230  		for j = 0; j < i; j++ {
   231  			if b.dims[j] != a.dims[j]*x {
   232  				t.Error("In-place Scalar Multiplication failed, ",
   233  					"didn't get expected values.")
   234  				t.Logf("%f * %f != %f", a.dims[j], x, b.dims[j])
   235  			}
   236  		}
   237  	}
   238  }
   239  
   240  // Creates pseudo-random vectors, normalizes them both in-place and
   241  // non-destructive, and verifies that the result is correct.
   242  func TestNormalize(t *testing.T) {
   243  	var i uint
   244  	// It makes no sense to normalize a zero vector, therefore we start at 1.
   245  	for i = 1; i < 100; i++ {
   246  		a := makeRandomVector(i)
   247  		b := Normalize(a)
   248  
   249  		if b.Len() != float64(1) {
   250  			t.Error("Normalization failed, vector doesn't have length 1.")
   251  			t.Logf("%f != 1", b.Len())
   252  		}
   253  	}
   254  }
   255  
   256  // Uses vectors with known angles to calculate their DotProduct, then verifies
   257  // if the result is correct.
   258  func TestDotProduct(t *testing.T) {
   259  	a := New(2)
   260  	b := New(2)
   261  
   262  	// SetDBObject the vectors as parallel.
   263  	a.Set(0, 1)
   264  	b.Set(0, 1)
   265  	dot, _ := DotProduct(a, b)
   266  	if dot != 1 {
   267  		t.Error("Dot Product of parallel vectors isn't 1.")
   268  	}
   269  
   270  	// SetDBObject the vectors as orthogonal.
   271  	b = New(2)
   272  	b.Set(1, 1)
   273  	dot, _ = DotProduct(a, b)
   274  	if dot != 0 {
   275  		t.Error("Dot Product of orthogonal vectors isn't 0.")
   276  	}
   277  
   278  	// SetDBObject the vectors as anti-parallel.
   279  	b = New(2)
   280  	b.Set(0, -1)
   281  	dot, _ = DotProduct(a, b)
   282  	if dot != -1 {
   283  		t.Error("Dot Product of anti-parallel vectors isn't -1.")
   284  	}
   285  }
   286  
   287  // Uses vectors with known angles to verify that Angle() is correct.
   288  func TestAngle(t *testing.T) {
   289  	a := New(2)
   290  	b := New(2)
   291  
   292  	// SetDBObject the vectors as parallel (Θ == 0).
   293  	a.Set(0, 1)
   294  	b.Set(0, 1)
   295  	Θ, _ := Angle(a, b)
   296  	if Θ != 0 {
   297  		t.Error("Angle between parallel vectors isn't 0.")
   298  		t.Logf("%f != 0", Θ)
   299  	}
   300  
   301  	// SetDBObject the vectors as orthogonal (Θ == 0.5π).
   302  	b = New(2)
   303  	b.Set(1, 1)
   304  	Θ, _ = Angle(a, b)
   305  	if Θ != 0.5*math.Pi {
   306  		t.Error("Angle between orthonal vectors isn't 0.5π.")
   307  		t.Logf("%f != %f", Θ, 0.5*math.Pi)
   308  	}
   309  
   310  	// SetDBObject the vectors as anti-parallel (Θ == π).
   311  	b = New(2)
   312  	b.Set(0, -1)
   313  	Θ, _ = Angle(a, b)
   314  	if Θ != math.Pi {
   315  		t.Error("Angle between anti-parallel vectors isn't π.")
   316  		t.Logf("%f != %f", Θ, math.Pi)
   317  	}
   318  }
   319  
   320  // Calculates the cross product of two pseudo-random vectors, then checks if
   321  // the resulting vector is orthogonal to both the original vectors. Tests both
   322  // in-place and non-destructive versions of the operation.
   323  func TestCrossProduct(t *testing.T) {
   324  	check := func(a, b, c *Vector) {
   325  		dot_a, _ := DotProduct(a, c)
   326  		dot_b, _ := DotProduct(b, c)
   327  		ε := 0.0000000005
   328  		if math.Abs(0-dot_a) < ε {
   329  			dot_a = 0
   330  		}
   331  		if math.Abs(0-dot_b) < ε {
   332  			dot_b = 0
   333  		}
   334  		if dot_a != 0 || dot_b != 0 {
   335  			t.Error("Either or both vectors aren't orthogonal",
   336  				"to their Cross Product.")
   337  			t.Logf("a * c = %f", dot_a)
   338  			t.Logf("b * c = %f", dot_b)
   339  		}
   340  	}
   341  
   342  	a := makeRandomVector(3)
   343  	b := makeRandomVector(3)
   344  	c, _ := CrossProduct(a, b)
   345  
   346  	check(a, b, c)
   347  
   348  	// Check in-place, too.
   349  	c = a.Copy()
   350  	c.CrossProduct(b)
   351  
   352  	check(a, b, c)
   353  
   354  	// Check if vectors ∉ ℝ³ are rejected.
   355  	d := New(2)
   356  	e := New(4)
   357  	_, err := CrossProduct(d, e)
   358  	if err == nil {
   359  		t.Error("CrossProduct() didn't error with invalid input vectors",
   360  			"(∉ ℝ³)")
   361  	}
   362  }
   363  
   364  // Check whether the various functions that take more than one vector error on
   365  // being supplied with vectors of missmatched dimensions.
   366  // It suffices to check the helper function checkDims, since every function
   367  // must call it to verify its inputs.
   368  func TestMissmatchedDims(t *testing.T) {
   369  	a := New(2)
   370  	b := New(3)
   371  
   372  	err := checkDims(a, b)
   373  	if err == nil {
   374  		t.Error("Missmatched dimension check succeeded on unequal dimensions.")
   375  	}
   376  
   377  	a = New(4)
   378  	b = New(4)
   379  	err = checkDims(a, b)
   380  	if err != nil {
   381  		t.Error("Missmatched dimension check failed on equal dimensions.")
   382  	}
   383  }
   384  
   385  // =========================== [ Helper Functions ] ===========================
   386  
   387  // Helper function, makes pseudo-random slices.
   388  func makeRandSlice(length uint) (randslice []float64) {
   389  	randslice = make([]float64, length)
   390  	for i := range randslice {
   391  		randslice[i] = rand.ExpFloat64()
   392  	}
   393  	return
   394  }
   395  
   396  // Helper function, make a pseudo-random Vector with dimension dim.
   397  func makeRandomVector(dim uint) *Vector {
   398  	return NewFrom(makeRandSlice(dim))
   399  }