gonum.org/v1/gonum@v0.14.0/cmplxs/cmplxs.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 cmplxs 6 7 import ( 8 "errors" 9 "math" 10 "math/cmplx" 11 12 "gonum.org/v1/gonum/cmplxs/cscalar" 13 "gonum.org/v1/gonum/internal/asm/c128" 14 ) 15 16 const ( 17 zeroLength = "cmplxs: zero length slice" 18 shortSpan = "cmplxs: slice length less than 2" 19 badLength = "cmplxs: slice lengths do not match" 20 badDstLength = "cmplxs: destination slice length does not match input" 21 ) 22 23 // Abs calculates the absolute values of the elements of s, and stores them in dst. 24 // It panics if the argument lengths do not match. 25 func Abs(dst []float64, s []complex128) { 26 if len(dst) != len(s) { 27 panic(badDstLength) 28 } 29 for i, v := range s { 30 dst[i] = cmplx.Abs(v) 31 } 32 } 33 34 // Add adds, element-wise, the elements of s and dst, and stores the result in dst. 35 // It panics if the argument lengths do not match. 36 func Add(dst, s []complex128) { 37 if len(dst) != len(s) { 38 panic(badLength) 39 } 40 c128.AxpyUnitaryTo(dst, 1, s, dst) 41 } 42 43 // AddTo adds, element-wise, the elements of s and t and 44 // stores the result in dst. 45 // It panics if the argument lengths do not match. 46 func AddTo(dst, s, t []complex128) []complex128 { 47 if len(s) != len(t) { 48 panic(badLength) 49 } 50 if len(dst) != len(s) { 51 panic(badDstLength) 52 } 53 c128.AxpyUnitaryTo(dst, 1, s, t) 54 return dst 55 } 56 57 // AddConst adds the scalar c to all of the values in dst. 58 func AddConst(c complex128, dst []complex128) { 59 c128.AddConst(c, dst) 60 } 61 62 // AddScaled performs dst = dst + alpha * s. 63 // It panics if the slice argument lengths do not match. 64 func AddScaled(dst []complex128, alpha complex128, s []complex128) { 65 if len(dst) != len(s) { 66 panic(badLength) 67 } 68 c128.AxpyUnitaryTo(dst, alpha, s, dst) 69 } 70 71 // AddScaledTo performs dst = y + alpha * s, where alpha is a scalar, 72 // and dst, y and s are all slices. 73 // It panics if the slice argument lengths do not match. 74 // 75 // At the return of the function, dst[i] = y[i] + alpha * s[i] 76 func AddScaledTo(dst, y []complex128, alpha complex128, s []complex128) []complex128 { 77 if len(s) != len(y) { 78 panic(badLength) 79 } 80 if len(dst) != len(y) { 81 panic(badDstLength) 82 } 83 c128.AxpyUnitaryTo(dst, alpha, s, y) 84 return dst 85 } 86 87 // Count applies the function f to every element of s and returns the number 88 // of times the function returned true. 89 func Count(f func(complex128) bool, s []complex128) int { 90 var n int 91 for _, val := range s { 92 if f(val) { 93 n++ 94 } 95 } 96 return n 97 } 98 99 // Complex fills each of the elements of dst with the complex number 100 // constructed from the corresponding elements of real and imag. 101 // It panics if the argument lengths do not match. 102 func Complex(dst []complex128, real, imag []float64) []complex128 { 103 if len(real) != len(imag) { 104 panic(badLength) 105 } 106 if len(dst) != len(real) { 107 panic(badDstLength) 108 } 109 if len(dst) == 0 { 110 return dst 111 } 112 for i, r := range real { 113 dst[i] = complex(r, imag[i]) 114 } 115 return dst 116 } 117 118 // CumProd finds the cumulative product of elements of s and store it in 119 // place into dst so that 120 // 121 // dst[i] = s[i] * s[i-1] * s[i-2] * ... * s[0] 122 // 123 // It panics if the argument lengths do not match. 124 func CumProd(dst, s []complex128) []complex128 { 125 if len(dst) != len(s) { 126 panic(badDstLength) 127 } 128 if len(dst) == 0 { 129 return dst 130 } 131 return c128.CumProd(dst, s) 132 } 133 134 // CumSum finds the cumulative sum of elements of s and stores it in place 135 // into dst so that 136 // 137 // dst[i] = s[i] + s[i-1] + s[i-2] + ... + s[0] 138 // 139 // It panics if the argument lengths do not match. 140 func CumSum(dst, s []complex128) []complex128 { 141 if len(dst) != len(s) { 142 panic(badDstLength) 143 } 144 if len(dst) == 0 { 145 return dst 146 } 147 return c128.CumSum(dst, s) 148 } 149 150 // Distance computes the L-norm of s - t. See Norm for special cases. 151 // It panics if the slice argument lengths do not match. 152 func Distance(s, t []complex128, L float64) float64 { 153 if len(s) != len(t) { 154 panic(badLength) 155 } 156 if len(s) == 0 { 157 return 0 158 } 159 160 var norm float64 161 switch { 162 case L == 2: 163 return c128.L2DistanceUnitary(s, t) 164 case L == 1: 165 for i, v := range s { 166 norm += cmplx.Abs(t[i] - v) 167 } 168 return norm 169 case math.IsInf(L, 1): 170 for i, v := range s { 171 absDiff := cmplx.Abs(t[i] - v) 172 if absDiff > norm { 173 norm = absDiff 174 } 175 } 176 return norm 177 default: 178 for i, v := range s { 179 norm += math.Pow(cmplx.Abs(t[i]-v), L) 180 } 181 return math.Pow(norm, 1/L) 182 } 183 } 184 185 // Div performs element-wise division dst / s 186 // and stores the result in dst. 187 // It panics if the argument lengths do not match. 188 func Div(dst, s []complex128) { 189 if len(dst) != len(s) { 190 panic(badLength) 191 } 192 c128.Div(dst, s) 193 } 194 195 // DivTo performs element-wise division s / t 196 // and stores the result in dst. 197 // It panics if the argument lengths do not match. 198 func DivTo(dst, s, t []complex128) []complex128 { 199 if len(s) != len(t) { 200 panic(badLength) 201 } 202 if len(dst) != len(s) { 203 panic(badDstLength) 204 } 205 return c128.DivTo(dst, s, t) 206 } 207 208 // Dot computes the dot product of s1 and s2, i.e. 209 // sum_{i = 1}^N conj(s1[i])*s2[i]. 210 // It panics if the argument lengths do not match. 211 func Dot(s1, s2 []complex128) complex128 { 212 if len(s1) != len(s2) { 213 panic(badLength) 214 } 215 return c128.DotUnitary(s1, s2) 216 } 217 218 // Equal returns true when the slices have equal lengths and 219 // all elements are numerically identical. 220 func Equal(s1, s2 []complex128) bool { 221 if len(s1) != len(s2) { 222 return false 223 } 224 for i, val := range s1 { 225 if s2[i] != val { 226 return false 227 } 228 } 229 return true 230 } 231 232 // EqualApprox returns true when the slices have equal lengths and 233 // all element pairs have an absolute tolerance less than tol or a 234 // relative tolerance less than tol. 235 func EqualApprox(s1, s2 []complex128, tol float64) bool { 236 if len(s1) != len(s2) { 237 return false 238 } 239 for i, a := range s1 { 240 if !cscalar.EqualWithinAbsOrRel(a, s2[i], tol, tol) { 241 return false 242 } 243 } 244 return true 245 } 246 247 // EqualFunc returns true when the slices have the same lengths 248 // and the function returns true for all element pairs. 249 func EqualFunc(s1, s2 []complex128, f func(complex128, complex128) bool) bool { 250 if len(s1) != len(s2) { 251 return false 252 } 253 for i, val := range s1 { 254 if !f(val, s2[i]) { 255 return false 256 } 257 } 258 return true 259 } 260 261 // EqualLengths returns true when all of the slices have equal length, 262 // and false otherwise. It also returns true when there are no input slices. 263 func EqualLengths(slices ...[]complex128) bool { 264 // This length check is needed: http://play.golang.org/p/sdty6YiLhM 265 if len(slices) == 0 { 266 return true 267 } 268 l := len(slices[0]) 269 for i := 1; i < len(slices); i++ { 270 if len(slices[i]) != l { 271 return false 272 } 273 } 274 return true 275 } 276 277 // Find applies f to every element of s and returns the indices of the first 278 // k elements for which the f returns true, or all such elements 279 // if k < 0. 280 // Find will reslice inds to have 0 length, and will append 281 // found indices to inds. 282 // If k > 0 and there are fewer than k elements in s satisfying f, 283 // all of the found elements will be returned along with an error. 284 // At the return of the function, the input inds will be in an undetermined state. 285 func Find(inds []int, f func(complex128) bool, s []complex128, k int) ([]int, error) { 286 // inds is also returned to allow for calling with nil. 287 288 // Reslice inds to have zero length. 289 inds = inds[:0] 290 291 // If zero elements requested, can just return. 292 if k == 0 { 293 return inds, nil 294 } 295 296 // If k < 0, return all of the found indices. 297 if k < 0 { 298 for i, val := range s { 299 if f(val) { 300 inds = append(inds, i) 301 } 302 } 303 return inds, nil 304 } 305 306 // Otherwise, find the first k elements. 307 nFound := 0 308 for i, val := range s { 309 if f(val) { 310 inds = append(inds, i) 311 nFound++ 312 if nFound == k { 313 return inds, nil 314 } 315 } 316 } 317 // Finished iterating over the loop, which means k elements were not found. 318 return inds, errors.New("cmplxs: insufficient elements found") 319 } 320 321 // HasNaN returns true when the slice s has any values that are NaN and false 322 // otherwise. 323 func HasNaN(s []complex128) bool { 324 for _, v := range s { 325 if cmplx.IsNaN(v) { 326 return true 327 } 328 } 329 return false 330 } 331 332 // Imag places the imaginary components of src into dst. 333 // It panics if the argument lengths do not match. 334 func Imag(dst []float64, src []complex128) []float64 { 335 if len(dst) != len(src) { 336 panic(badDstLength) 337 } 338 if len(dst) == 0 { 339 return dst 340 } 341 for i, z := range src { 342 dst[i] = imag(z) 343 } 344 return dst 345 } 346 347 // LogSpan returns a set of n equally spaced points in log space between, 348 // l and u where N is equal to len(dst). The first element of the 349 // resulting dst will be l and the final element of dst will be u. 350 // Panics if len(dst) < 2 351 // Note that this call will return NaNs if either l or u are negative, and 352 // will return all zeros if l or u is zero. 353 // Also returns the mutated slice dst, so that it can be used in range, like: 354 // 355 // for i, x := range LogSpan(dst, l, u) { ... } 356 func LogSpan(dst []complex128, l, u complex128) []complex128 { 357 Span(dst, cmplx.Log(l), cmplx.Log(u)) 358 for i := range dst { 359 dst[i] = cmplx.Exp(dst[i]) 360 } 361 return dst 362 } 363 364 // MaxAbs returns the maximum absolute value in the input slice. 365 // It panics if s is zero length. 366 func MaxAbs(s []complex128) complex128 { 367 return s[MaxAbsIdx(s)] 368 } 369 370 // MaxAbsIdx returns the index of the maximum absolute value in the input slice. 371 // If several entries have the maximum absolute value, the first such index is 372 // returned. 373 // It panics if s is zero length. 374 func MaxAbsIdx(s []complex128) int { 375 if len(s) == 0 { 376 panic(zeroLength) 377 } 378 max := math.NaN() 379 var ind int 380 for i, v := range s { 381 if cmplx.IsNaN(v) { 382 continue 383 } 384 if a := cmplx.Abs(v); a > max || math.IsNaN(max) { 385 max = a 386 ind = i 387 } 388 } 389 return ind 390 } 391 392 // MinAbs returns the minimum absolute value in the input slice. 393 // It panics if s is zero length. 394 func MinAbs(s []complex128) complex128 { 395 return s[MinAbsIdx(s)] 396 } 397 398 // MinAbsIdx returns the index of the minimum absolute value in the input slice. If several 399 // entries have the minimum absolute value, the first such index is returned. 400 // It panics if s is zero length. 401 func MinAbsIdx(s []complex128) int { 402 if len(s) == 0 { 403 panic(zeroLength) 404 } 405 min := math.NaN() 406 var ind int 407 for i, v := range s { 408 if cmplx.IsNaN(v) { 409 continue 410 } 411 if a := cmplx.Abs(v); a < min || math.IsNaN(min) { 412 min = a 413 ind = i 414 } 415 } 416 return ind 417 } 418 419 // Mul performs element-wise multiplication between dst 420 // and s and stores the result in dst. 421 // It panics if the argument lengths do not match. 422 func Mul(dst, s []complex128) { 423 if len(dst) != len(s) { 424 panic(badLength) 425 } 426 for i, val := range s { 427 dst[i] *= val 428 } 429 } 430 431 // MulConj performs element-wise multiplication between dst 432 // and the conjugate of s and stores the result in dst. 433 // It panics if the argument lengths do not match. 434 func MulConj(dst, s []complex128) { 435 if len(dst) != len(s) { 436 panic(badLength) 437 } 438 for i, val := range s { 439 dst[i] *= cmplx.Conj(val) 440 } 441 } 442 443 // MulConjTo performs element-wise multiplication between s 444 // and the conjugate of t and stores the result in dst. 445 // It panics if the argument lengths do not match. 446 func MulConjTo(dst, s, t []complex128) []complex128 { 447 if len(s) != len(t) { 448 panic(badLength) 449 } 450 if len(dst) != len(s) { 451 panic(badDstLength) 452 } 453 for i, val := range t { 454 dst[i] = cmplx.Conj(val) * s[i] 455 } 456 return dst 457 } 458 459 // MulTo performs element-wise multiplication between s 460 // and t and stores the result in dst. 461 // It panics if the argument lengths do not match. 462 func MulTo(dst, s, t []complex128) []complex128 { 463 if len(s) != len(t) { 464 panic(badLength) 465 } 466 if len(dst) != len(s) { 467 panic(badDstLength) 468 } 469 for i, val := range t { 470 dst[i] = val * s[i] 471 } 472 return dst 473 } 474 475 // NearestIdx returns the index of the element in s 476 // whose value is nearest to v. If several such 477 // elements exist, the lowest index is returned. 478 // It panics if s is zero length. 479 func NearestIdx(s []complex128, v complex128) int { 480 if len(s) == 0 { 481 panic(zeroLength) 482 } 483 switch { 484 case cmplx.IsNaN(v): 485 return 0 486 case cmplx.IsInf(v): 487 return MaxAbsIdx(s) 488 } 489 var ind int 490 dist := math.NaN() 491 for i, val := range s { 492 newDist := cmplx.Abs(v - val) 493 // A NaN distance will not be closer. 494 if math.IsNaN(newDist) { 495 continue 496 } 497 if newDist < dist || math.IsNaN(dist) { 498 dist = newDist 499 ind = i 500 } 501 } 502 return ind 503 } 504 505 // Norm returns the L-norm of the slice S, defined as 506 // (sum_{i=1}^N abs(s[i])^L)^{1/L} 507 // Special cases: 508 // L = math.Inf(1) gives the maximum absolute value. 509 // Does not correctly compute the zero norm (use Count). 510 func Norm(s []complex128, L float64) float64 { 511 // Should this complain if L is not positive? 512 // Should this be done in log space for better numerical stability? 513 // would be more cost 514 // maybe only if L is high? 515 if len(s) == 0 { 516 return 0 517 } 518 var norm float64 519 switch { 520 case L == 2: 521 return c128.L2NormUnitary(s) 522 case L == 1: 523 for _, v := range s { 524 norm += cmplx.Abs(v) 525 } 526 return norm 527 case math.IsInf(L, 1): 528 for _, v := range s { 529 norm = math.Max(norm, cmplx.Abs(v)) 530 } 531 return norm 532 default: 533 for _, v := range s { 534 norm += math.Pow(cmplx.Abs(v), L) 535 } 536 return math.Pow(norm, 1/L) 537 } 538 } 539 540 // Prod returns the product of the elements of the slice. 541 // Returns 1 if len(s) = 0. 542 func Prod(s []complex128) complex128 { 543 prod := 1 + 0i 544 for _, val := range s { 545 prod *= val 546 } 547 return prod 548 } 549 550 // Real places the real components of src into dst. 551 // It panics if the argument lengths do not match. 552 func Real(dst []float64, src []complex128) []float64 { 553 if len(dst) != len(src) { 554 panic(badDstLength) 555 } 556 if len(dst) == 0 { 557 return dst 558 } 559 for i, z := range src { 560 dst[i] = real(z) 561 } 562 return dst 563 } 564 565 // Reverse reverses the order of elements in the slice. 566 func Reverse(s []complex128) { 567 for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 568 s[i], s[j] = s[j], s[i] 569 } 570 } 571 572 // Same returns true when the input slices have the same length and all 573 // elements have the same value with NaN treated as the same. 574 func Same(s, t []complex128) bool { 575 if len(s) != len(t) { 576 return false 577 } 578 for i, v := range s { 579 w := t[i] 580 if v != w && !(cmplx.IsNaN(v) && cmplx.IsNaN(w)) { 581 return false 582 } 583 } 584 return true 585 } 586 587 // Scale multiplies every element in dst by the scalar c. 588 func Scale(c complex128, dst []complex128) { 589 if len(dst) > 0 { 590 c128.ScalUnitary(c, dst) 591 } 592 } 593 594 // ScaleReal multiplies every element in dst by the real scalar f. 595 func ScaleReal(f float64, dst []complex128) { 596 for i, z := range dst { 597 dst[i] = complex(f*real(z), f*imag(z)) 598 } 599 } 600 601 // ScaleRealTo multiplies the elements in s by the real scalar f and 602 // stores the result in dst. 603 // It panics if the slice argument lengths do not match. 604 func ScaleRealTo(dst []complex128, f float64, s []complex128) []complex128 { 605 if len(dst) != len(s) { 606 panic(badDstLength) 607 } 608 for i, z := range s { 609 dst[i] = complex(f*real(z), f*imag(z)) 610 } 611 return dst 612 } 613 614 // ScaleTo multiplies the elements in s by c and stores the result in dst. 615 // It panics if the slice argument lengths do not match. 616 func ScaleTo(dst []complex128, c complex128, s []complex128) []complex128 { 617 if len(dst) != len(s) { 618 panic(badDstLength) 619 } 620 if len(dst) > 0 { 621 c128.ScalUnitaryTo(dst, c, s) 622 } 623 return dst 624 } 625 626 // Span returns a set of N equally spaced points between l and u, where N 627 // is equal to the length of the destination. The first element of the destination 628 // is l, the final element of the destination is u. 629 // It panics if the length of dst is less than 2. 630 // 631 // Span also returns the mutated slice dst, so that it can be used in range expressions, 632 // like: 633 // 634 // for i, x := range Span(dst, l, u) { ... } 635 func Span(dst []complex128, l, u complex128) []complex128 { 636 n := len(dst) 637 if n < 2 { 638 panic(shortSpan) 639 } 640 641 // Special cases for Inf and NaN. 642 switch { 643 case cmplx.IsNaN(l): 644 for i := range dst[:len(dst)-1] { 645 dst[i] = cmplx.NaN() 646 } 647 dst[len(dst)-1] = u 648 return dst 649 case cmplx.IsNaN(u): 650 for i := range dst[1:] { 651 dst[i+1] = cmplx.NaN() 652 } 653 dst[0] = l 654 return dst 655 case cmplx.IsInf(l) && cmplx.IsInf(u): 656 for i := range dst { 657 dst[i] = cmplx.Inf() 658 } 659 return dst 660 case cmplx.IsInf(l): 661 for i := range dst[:len(dst)-1] { 662 dst[i] = l 663 } 664 dst[len(dst)-1] = u 665 return dst 666 case cmplx.IsInf(u): 667 for i := range dst[1:] { 668 dst[i+1] = u 669 } 670 dst[0] = l 671 return dst 672 } 673 674 step := (u - l) / complex(float64(n-1), 0) 675 for i := range dst { 676 dst[i] = l + step*complex(float64(i), 0) 677 } 678 return dst 679 } 680 681 // Sub subtracts, element-wise, the elements of s from dst. 682 // It panics if the argument lengths do not match. 683 func Sub(dst, s []complex128) { 684 if len(dst) != len(s) { 685 panic(badLength) 686 } 687 c128.AxpyUnitaryTo(dst, -1, s, dst) 688 } 689 690 // SubTo subtracts, element-wise, the elements of t from s and 691 // stores the result in dst. 692 // It panics if the argument lengths do not match. 693 func SubTo(dst, s, t []complex128) []complex128 { 694 if len(s) != len(t) { 695 panic(badLength) 696 } 697 if len(dst) != len(s) { 698 panic(badDstLength) 699 } 700 c128.AxpyUnitaryTo(dst, -1, t, s) 701 return dst 702 } 703 704 // Sum returns the sum of the elements of the slice. 705 func Sum(s []complex128) complex128 { 706 return c128.Sum(s) 707 }