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  }