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