github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/floats/floats.go (about) 1 // Copyright ©2013 The Gonum Authors. All rights reserved. 2 // Use of this code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package floats 6 7 import ( 8 "errors" 9 "math" 10 "sort" 11 12 "github.com/jingcheng-WU/gonum/floats/scalar" 13 "github.com/jingcheng-WU/gonum/internal/asm/f64" 14 ) 15 16 const ( 17 zeroLength = "floats: zero length slice" 18 shortSpan = "floats: slice length less than 2" 19 badLength = "floats: slice lengths do not match" 20 badDstLength = "floats: destination slice length does not match input" 21 ) 22 23 // Add adds, element-wise, the elements of s and dst, and stores the result in dst. 24 // It panics if the argument lengths do not match. 25 func Add(dst, s []float64) { 26 if len(dst) != len(s) { 27 panic(badDstLength) 28 } 29 f64.AxpyUnitaryTo(dst, 1, s, dst) 30 } 31 32 // AddTo adds, element-wise, the elements of s and t and 33 // stores the result in dst. 34 // It panics if the argument lengths do not match. 35 func AddTo(dst, s, t []float64) []float64 { 36 if len(s) != len(t) { 37 panic(badLength) 38 } 39 if len(dst) != len(s) { 40 panic(badDstLength) 41 } 42 f64.AxpyUnitaryTo(dst, 1, s, t) 43 return dst 44 } 45 46 // AddConst adds the scalar c to all of the values in dst. 47 func AddConst(c float64, dst []float64) { 48 f64.AddConst(c, dst) 49 } 50 51 // AddScaled performs dst = dst + alpha * s. 52 // It panics if the slice argument lengths do not match. 53 func AddScaled(dst []float64, alpha float64, s []float64) { 54 if len(dst) != len(s) { 55 panic(badLength) 56 } 57 f64.AxpyUnitaryTo(dst, alpha, s, dst) 58 } 59 60 // AddScaledTo performs dst = y + alpha * s, where alpha is a scalar, 61 // and dst, y and s are all slices. 62 // It panics if the slice argument lengths do not match. 63 // 64 // At the return of the function, dst[i] = y[i] + alpha * s[i] 65 func AddScaledTo(dst, y []float64, alpha float64, s []float64) []float64 { 66 if len(s) != len(y) { 67 panic(badLength) 68 } 69 if len(dst) != len(y) { 70 panic(badDstLength) 71 } 72 f64.AxpyUnitaryTo(dst, alpha, s, y) 73 return dst 74 } 75 76 // argsort is a helper that implements sort.Interface, as used by 77 // Argsort. 78 type argsort struct { 79 s []float64 80 inds []int 81 } 82 83 func (a argsort) Len() int { 84 return len(a.s) 85 } 86 87 func (a argsort) Less(i, j int) bool { 88 return a.s[i] < a.s[j] 89 } 90 91 func (a argsort) Swap(i, j int) { 92 a.s[i], a.s[j] = a.s[j], a.s[i] 93 a.inds[i], a.inds[j] = a.inds[j], a.inds[i] 94 } 95 96 // Argsort sorts the elements of dst while tracking their original order. 97 // At the conclusion of Argsort, dst will contain the original elements of dst 98 // but sorted in increasing order, and inds will contain the original position 99 // of the elements in the slice such that dst[i] = origDst[inds[i]]. 100 // It panics if the argument lengths do not match. 101 func Argsort(dst []float64, inds []int) { 102 if len(dst) != len(inds) { 103 panic(badDstLength) 104 } 105 for i := range dst { 106 inds[i] = i 107 } 108 109 a := argsort{s: dst, inds: inds} 110 sort.Sort(a) 111 } 112 113 // Count applies the function f to every element of s and returns the number 114 // of times the function returned true. 115 func Count(f func(float64) bool, s []float64) int { 116 var n int 117 for _, val := range s { 118 if f(val) { 119 n++ 120 } 121 } 122 return n 123 } 124 125 // CumProd finds the cumulative product of the first i elements in 126 // s and puts them in place into the ith element of the 127 // destination dst. 128 // It panics if the argument lengths do not match. 129 // 130 // At the return of the function, dst[i] = s[i] * s[i-1] * s[i-2] * ... 131 func CumProd(dst, s []float64) []float64 { 132 if len(dst) != len(s) { 133 panic(badDstLength) 134 } 135 if len(dst) == 0 { 136 return dst 137 } 138 return f64.CumProd(dst, s) 139 } 140 141 // CumSum finds the cumulative sum of the first i elements in 142 // s and puts them in place into the ith element of the 143 // destination dst. 144 // It panics if the argument lengths do not match. 145 // 146 // At the return of the function, dst[i] = s[i] + s[i-1] + s[i-2] + ... 147 func CumSum(dst, s []float64) []float64 { 148 if len(dst) != len(s) { 149 panic(badDstLength) 150 } 151 if len(dst) == 0 { 152 return dst 153 } 154 return f64.CumSum(dst, s) 155 } 156 157 // Distance computes the L-norm of s - t. See Norm for special cases. 158 // It panics if the slice argument lengths do not match. 159 func Distance(s, t []float64, L float64) float64 { 160 if len(s) != len(t) { 161 panic(badLength) 162 } 163 if len(s) == 0 { 164 return 0 165 } 166 if L == 2 { 167 return f64.L2DistanceUnitary(s, t) 168 } 169 var norm float64 170 if L == 1 { 171 for i, v := range s { 172 norm += math.Abs(t[i] - v) 173 } 174 return norm 175 } 176 if math.IsInf(L, 1) { 177 for i, v := range s { 178 absDiff := math.Abs(t[i] - v) 179 if absDiff > norm { 180 norm = absDiff 181 } 182 } 183 return norm 184 } 185 for i, v := range s { 186 norm += math.Pow(math.Abs(t[i]-v), L) 187 } 188 return math.Pow(norm, 1/L) 189 } 190 191 // Div performs element-wise division dst / s 192 // and stores the value in dst. 193 // It panics if the argument lengths do not match. 194 func Div(dst, s []float64) { 195 if len(dst) != len(s) { 196 panic(badLength) 197 } 198 f64.Div(dst, s) 199 } 200 201 // DivTo performs element-wise division s / t 202 // and stores the value in dst. 203 // It panics if the argument lengths do not match. 204 func DivTo(dst, s, t []float64) []float64 { 205 if len(s) != len(t) { 206 panic(badLength) 207 } 208 if len(dst) != len(s) { 209 panic(badDstLength) 210 } 211 return f64.DivTo(dst, s, t) 212 } 213 214 // Dot computes the dot product of s1 and s2, i.e. 215 // sum_{i = 1}^N s1[i]*s2[i]. 216 // It panics if the argument lengths do not match. 217 func Dot(s1, s2 []float64) float64 { 218 if len(s1) != len(s2) { 219 panic(badLength) 220 } 221 return f64.DotUnitary(s1, s2) 222 } 223 224 // Equal returns true when the slices have equal lengths and 225 // all elements are numerically identical. 226 func Equal(s1, s2 []float64) bool { 227 if len(s1) != len(s2) { 228 return false 229 } 230 for i, val := range s1 { 231 if s2[i] != val { 232 return false 233 } 234 } 235 return true 236 } 237 238 // EqualApprox returns true when the slices have equal lengths and 239 // all element pairs have an absolute tolerance less than tol or a 240 // relative tolerance less than tol. 241 func EqualApprox(s1, s2 []float64, tol float64) bool { 242 if len(s1) != len(s2) { 243 return false 244 } 245 for i, a := range s1 { 246 if !scalar.EqualWithinAbsOrRel(a, s2[i], tol, tol) { 247 return false 248 } 249 } 250 return true 251 } 252 253 // EqualFunc returns true when the slices have the same lengths 254 // and the function returns true for all element pairs. 255 func EqualFunc(s1, s2 []float64, f func(float64, float64) bool) bool { 256 if len(s1) != len(s2) { 257 return false 258 } 259 for i, val := range s1 { 260 if !f(val, s2[i]) { 261 return false 262 } 263 } 264 return true 265 } 266 267 // EqualLengths returns true when all of the slices have equal length, 268 // and false otherwise. It also returns true when there are no input slices. 269 func EqualLengths(slices ...[]float64) bool { 270 // This length check is needed: http://play.golang.org/p/sdty6YiLhM 271 if len(slices) == 0 { 272 return true 273 } 274 l := len(slices[0]) 275 for i := 1; i < len(slices); i++ { 276 if len(slices[i]) != l { 277 return false 278 } 279 } 280 return true 281 } 282 283 // Find applies f to every element of s and returns the indices of the first 284 // k elements for which the f returns true, or all such elements 285 // if k < 0. 286 // Find will reslice inds to have 0 length, and will append 287 // found indices to inds. 288 // If k > 0 and there are fewer than k elements in s satisfying f, 289 // all of the found elements will be returned along with an error. 290 // At the return of the function, the input inds will be in an undetermined state. 291 func Find(inds []int, f func(float64) bool, s []float64, k int) ([]int, error) { 292 // inds is also returned to allow for calling with nil. 293 294 // Reslice inds to have zero length. 295 inds = inds[:0] 296 297 // If zero elements requested, can just return. 298 if k == 0 { 299 return inds, nil 300 } 301 302 // If k < 0, return all of the found indices. 303 if k < 0 { 304 for i, val := range s { 305 if f(val) { 306 inds = append(inds, i) 307 } 308 } 309 return inds, nil 310 } 311 312 // Otherwise, find the first k elements. 313 nFound := 0 314 for i, val := range s { 315 if f(val) { 316 inds = append(inds, i) 317 nFound++ 318 if nFound == k { 319 return inds, nil 320 } 321 } 322 } 323 // Finished iterating over the loop, which means k elements were not found. 324 return inds, errors.New("floats: insufficient elements found") 325 } 326 327 // HasNaN returns true when the slice s has any values that are NaN and false 328 // otherwise. 329 func HasNaN(s []float64) bool { 330 for _, v := range s { 331 if math.IsNaN(v) { 332 return true 333 } 334 } 335 return false 336 } 337 338 // LogSpan returns a set of n equally spaced points in log space between, 339 // l and u where N is equal to len(dst). The first element of the 340 // resulting dst will be l and the final element of dst will be u. 341 // It panics if the length of dst is less than 2. 342 // Note that this call will return NaNs if either l or u are negative, and 343 // will return all zeros if l or u is zero. 344 // Also returns the mutated slice dst, so that it can be used in range, like: 345 // 346 // for i, x := range LogSpan(dst, l, u) { ... } 347 func LogSpan(dst []float64, l, u float64) []float64 { 348 Span(dst, math.Log(l), math.Log(u)) 349 for i := range dst { 350 dst[i] = math.Exp(dst[i]) 351 } 352 return dst 353 } 354 355 // LogSumExp returns the log of the sum of the exponentials of the values in s. 356 // Panics if s is an empty slice. 357 func LogSumExp(s []float64) float64 { 358 // Want to do this in a numerically stable way which avoids 359 // overflow and underflow 360 // First, find the maximum value in the slice. 361 maxval := Max(s) 362 if math.IsInf(maxval, 0) { 363 // If it's infinity either way, the logsumexp will be infinity as well 364 // returning now avoids NaNs 365 return maxval 366 } 367 var lse float64 368 // Compute the sumexp part 369 for _, val := range s { 370 lse += math.Exp(val - maxval) 371 } 372 // Take the log and add back on the constant taken out 373 return math.Log(lse) + maxval 374 } 375 376 // Max returns the maximum value in the input slice. If the slice is empty, Max will panic. 377 func Max(s []float64) float64 { 378 return s[MaxIdx(s)] 379 } 380 381 // MaxIdx returns the index of the maximum value in the input slice. If several 382 // entries have the maximum value, the first such index is returned. 383 // It panics if s is zero length. 384 func MaxIdx(s []float64) int { 385 if len(s) == 0 { 386 panic(zeroLength) 387 } 388 max := math.NaN() 389 var ind int 390 for i, v := range s { 391 if math.IsNaN(v) { 392 continue 393 } 394 if v > max || math.IsNaN(max) { 395 max = v 396 ind = i 397 } 398 } 399 return ind 400 } 401 402 // Min returns the minimum value in the input slice. 403 // It panics if s is zero length. 404 func Min(s []float64) float64 { 405 return s[MinIdx(s)] 406 } 407 408 // MinIdx returns the index of the minimum value in the input slice. If several 409 // entries have the minimum value, the first such index is returned. 410 // It panics if s is zero length. 411 func MinIdx(s []float64) int { 412 if len(s) == 0 { 413 panic(zeroLength) 414 } 415 min := math.NaN() 416 var ind int 417 for i, v := range s { 418 if math.IsNaN(v) { 419 continue 420 } 421 if v < min || math.IsNaN(min) { 422 min = v 423 ind = i 424 } 425 } 426 return ind 427 } 428 429 // Mul performs element-wise multiplication between dst 430 // and s and stores the value in dst. 431 // It panics if the argument lengths do not match. 432 func Mul(dst, s []float64) { 433 if len(dst) != len(s) { 434 panic(badLength) 435 } 436 for i, val := range s { 437 dst[i] *= val 438 } 439 } 440 441 // MulTo performs element-wise multiplication between s 442 // and t and stores the value in dst. 443 // It panics if the argument lengths do not match. 444 func MulTo(dst, s, t []float64) []float64 { 445 if len(s) != len(t) { 446 panic(badLength) 447 } 448 if len(dst) != len(s) { 449 panic(badDstLength) 450 } 451 for i, val := range t { 452 dst[i] = val * s[i] 453 } 454 return dst 455 } 456 457 // NearestIdx returns the index of the element in s 458 // whose value is nearest to v. If several such 459 // elements exist, the lowest index is returned. 460 // It panics if s is zero length. 461 func NearestIdx(s []float64, v float64) int { 462 if len(s) == 0 { 463 panic(zeroLength) 464 } 465 switch { 466 case math.IsNaN(v): 467 return 0 468 case math.IsInf(v, 1): 469 return MaxIdx(s) 470 case math.IsInf(v, -1): 471 return MinIdx(s) 472 } 473 var ind int 474 dist := math.NaN() 475 for i, val := range s { 476 newDist := math.Abs(v - val) 477 // A NaN distance will not be closer. 478 if math.IsNaN(newDist) { 479 continue 480 } 481 if newDist < dist || math.IsNaN(dist) { 482 dist = newDist 483 ind = i 484 } 485 } 486 return ind 487 } 488 489 // NearestIdxForSpan return the index of a hypothetical vector created 490 // by Span with length n and bounds l and u whose value is closest 491 // to v. That is, NearestIdxForSpan(n, l, u, v) is equivalent to 492 // Nearest(Span(make([]float64, n),l,u),v) without an allocation. 493 // It panics if n is less than two. 494 func NearestIdxForSpan(n int, l, u float64, v float64) int { 495 if n < 2 { 496 panic(shortSpan) 497 } 498 if math.IsNaN(v) { 499 return 0 500 } 501 502 // Special cases for Inf and NaN. 503 switch { 504 case math.IsNaN(l) && !math.IsNaN(u): 505 return n - 1 506 case math.IsNaN(u): 507 return 0 508 case math.IsInf(l, 0) && math.IsInf(u, 0): 509 if l == u { 510 return 0 511 } 512 if n%2 == 1 { 513 if !math.IsInf(v, 0) { 514 return n / 2 515 } 516 if math.Copysign(1, v) == math.Copysign(1, l) { 517 return 0 518 } 519 return n/2 + 1 520 } 521 if math.Copysign(1, v) == math.Copysign(1, l) { 522 return 0 523 } 524 return n / 2 525 case math.IsInf(l, 0): 526 if v == l { 527 return 0 528 } 529 return n - 1 530 case math.IsInf(u, 0): 531 if v == u { 532 return n - 1 533 } 534 return 0 535 case math.IsInf(v, -1): 536 if l <= u { 537 return 0 538 } 539 return n - 1 540 case math.IsInf(v, 1): 541 if u <= l { 542 return 0 543 } 544 return n - 1 545 } 546 547 // Special cases for v outside (l, u) and (u, l). 548 switch { 549 case l < u: 550 if v <= l { 551 return 0 552 } 553 if v >= u { 554 return n - 1 555 } 556 case l > u: 557 if v >= l { 558 return 0 559 } 560 if v <= u { 561 return n - 1 562 } 563 default: 564 return 0 565 } 566 567 // Can't guarantee anything about exactly halfway between 568 // because of floating point weirdness. 569 return int((float64(n)-1)/(u-l)*(v-l) + 0.5) 570 } 571 572 // Norm returns the L norm of the slice S, defined as 573 // (sum_{i=1}^N s[i]^L)^{1/L} 574 // Special cases: 575 // L = math.Inf(1) gives the maximum absolute value. 576 // Does not correctly compute the zero norm (use Count). 577 func Norm(s []float64, L float64) float64 { 578 // Should this complain if L is not positive? 579 // Should this be done in log space for better numerical stability? 580 // would be more cost 581 // maybe only if L is high? 582 if len(s) == 0 { 583 return 0 584 } 585 if L == 2 { 586 return f64.L2NormUnitary(s) 587 } 588 var norm float64 589 if L == 1 { 590 for _, val := range s { 591 norm += math.Abs(val) 592 } 593 return norm 594 } 595 if math.IsInf(L, 1) { 596 for _, val := range s { 597 norm = math.Max(norm, math.Abs(val)) 598 } 599 return norm 600 } 601 for _, val := range s { 602 norm += math.Pow(math.Abs(val), L) 603 } 604 return math.Pow(norm, 1/L) 605 } 606 607 // Prod returns the product of the elements of the slice. 608 // Returns 1 if len(s) = 0. 609 func Prod(s []float64) float64 { 610 prod := 1.0 611 for _, val := range s { 612 prod *= val 613 } 614 return prod 615 } 616 617 // Reverse reverses the order of elements in the slice. 618 func Reverse(s []float64) { 619 for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 620 s[i], s[j] = s[j], s[i] 621 } 622 } 623 624 // Same returns true when the input slices have the same length and all 625 // elements have the same value with NaN treated as the same. 626 func Same(s, t []float64) bool { 627 if len(s) != len(t) { 628 return false 629 } 630 for i, v := range s { 631 w := t[i] 632 if v != w && !(math.IsNaN(v) && math.IsNaN(w)) { 633 return false 634 } 635 } 636 return true 637 } 638 639 // Scale multiplies every element in dst by the scalar c. 640 func Scale(c float64, dst []float64) { 641 if len(dst) > 0 { 642 f64.ScalUnitary(c, dst) 643 } 644 } 645 646 // ScaleTo multiplies the elements in s by c and stores the result in dst. 647 // It panics if the slice argument lengths do not match. 648 func ScaleTo(dst []float64, c float64, s []float64) []float64 { 649 if len(dst) != len(s) { 650 panic(badDstLength) 651 } 652 if len(dst) > 0 { 653 f64.ScalUnitaryTo(dst, c, s) 654 } 655 return dst 656 } 657 658 // Span returns a set of N equally spaced points between l and u, where N 659 // is equal to the length of the destination. The first element of the destination 660 // is l, the final element of the destination is u. 661 // It panics if the length of dst is less than 2. 662 // 663 // Span also returns the mutated slice dst, so that it can be used in range expressions, 664 // like: 665 // 666 // for i, x := range Span(dst, l, u) { ... } 667 func Span(dst []float64, l, u float64) []float64 { 668 n := len(dst) 669 if n < 2 { 670 panic(shortSpan) 671 } 672 673 // Special cases for Inf and NaN. 674 switch { 675 case math.IsNaN(l): 676 for i := range dst[:len(dst)-1] { 677 dst[i] = math.NaN() 678 } 679 dst[len(dst)-1] = u 680 return dst 681 case math.IsNaN(u): 682 for i := range dst[1:] { 683 dst[i+1] = math.NaN() 684 } 685 dst[0] = l 686 return dst 687 case math.IsInf(l, 0) && math.IsInf(u, 0): 688 for i := range dst[:len(dst)/2] { 689 dst[i] = l 690 dst[len(dst)-i-1] = u 691 } 692 if len(dst)%2 == 1 { 693 if l != u { 694 dst[len(dst)/2] = 0 695 } else { 696 dst[len(dst)/2] = l 697 } 698 } 699 return dst 700 case math.IsInf(l, 0): 701 for i := range dst[:len(dst)-1] { 702 dst[i] = l 703 } 704 dst[len(dst)-1] = u 705 return dst 706 case math.IsInf(u, 0): 707 for i := range dst[1:] { 708 dst[i+1] = u 709 } 710 dst[0] = l 711 return dst 712 } 713 714 step := (u - l) / float64(n-1) 715 for i := range dst { 716 dst[i] = l + step*float64(i) 717 } 718 return dst 719 } 720 721 // Sub subtracts, element-wise, the elements of s from dst. 722 // It panics if the argument lengths do not match. 723 func Sub(dst, s []float64) { 724 if len(dst) != len(s) { 725 panic(badLength) 726 } 727 f64.AxpyUnitaryTo(dst, -1, s, dst) 728 } 729 730 // SubTo subtracts, element-wise, the elements of t from s and 731 // stores the result in dst. 732 // It panics if the argument lengths do not match. 733 func SubTo(dst, s, t []float64) []float64 { 734 if len(s) != len(t) { 735 panic(badLength) 736 } 737 if len(dst) != len(s) { 738 panic(badDstLength) 739 } 740 f64.AxpyUnitaryTo(dst, -1, t, s) 741 return dst 742 } 743 744 // Sum returns the sum of the elements of the slice. 745 func Sum(s []float64) float64 { 746 return f64.Sum(s) 747 } 748 749 // Within returns the first index i where s[i] <= v < s[i+1]. Within panics if: 750 // - len(s) < 2 751 // - s is not sorted 752 func Within(s []float64, v float64) int { 753 if len(s) < 2 { 754 panic(shortSpan) 755 } 756 if !sort.Float64sAreSorted(s) { 757 panic("floats: input slice not sorted") 758 } 759 if v < s[0] || v >= s[len(s)-1] || math.IsNaN(v) { 760 return -1 761 } 762 for i, f := range s[1:] { 763 if v < f { 764 return i 765 } 766 } 767 return -1 768 } 769 770 // SumCompensated returns the sum of the elements of the slice calculated with greater 771 // accuracy than Sum at the expense of additional computation. 772 func SumCompensated(s []float64) float64 { 773 // SumCompensated uses an improved version of Kahan's compensated 774 // summation algorithm proposed by Neumaier. 775 // See https://en.wikipedia.org/wiki/Kahan_summation_algorithm for details. 776 var sum, c float64 777 for _, x := range s { 778 // This type conversion is here to prevent a sufficiently smart compiler 779 // from optimising away these operations. 780 t := float64(sum + x) 781 if math.Abs(sum) >= math.Abs(x) { 782 c += (sum - t) + x 783 } else { 784 c += (x - t) + sum 785 } 786 sum = t 787 } 788 return sum + c 789 }