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 }