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