mirror of
				https://github.com/gonum/gonum.git
				synced 2025-10-31 02:26:59 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			521 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			521 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2013 The Gonum Authors. All rights reserved.
 | |
| // Use of this code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file
 | |
| 
 | |
| package floats
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"math"
 | |
| )
 | |
| 
 | |
| // Add returns the element-wise sum of all the slices with the
 | |
| // results stored in the first slice.
 | |
| // For computational efficiency, it is assumed that all of
 | |
| // the variadic arguments have the same length. If this is
 | |
| // in doubt, EqLen can be used.
 | |
| func Add(dst []float64, slices ...[]float64) []float64 {
 | |
| 	if len(slices) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if len(dst) != len(slices[0]) {
 | |
| 		panic("floats: length of destination does not match length of the slices")
 | |
| 	}
 | |
| 	for _, slice := range slices {
 | |
| 		for j, val := range slice {
 | |
| 			dst[j] += val
 | |
| 		}
 | |
| 	}
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // AddConst adds the value c to all of the values in s.
 | |
| func AddConst(c float64, s []float64) {
 | |
| 	for i := range s {
 | |
| 		s[i] += c
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // AddScaled performs dst = dst + alpha * s.
 | |
| // It panics if the lengths of dst and s are not equal.
 | |
| func AddScaled(dst []float64, alpha float64, s []float64) {
 | |
| 	if len(dst) != len(s) {
 | |
| 		panic("floats: length of destination and source to not match")
 | |
| 	}
 | |
| 	for i, val := range s {
 | |
| 		dst[i] += alpha * val
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // AddScaledTo performs dst = y + alpha * s.
 | |
| // It panics if the lengths of dst, y, and s are not equal.
 | |
| func AddScaledTo(dst []float64, y []float64, alpha float64, s []float64) []float64 {
 | |
| 	if len(dst) != len(s) || len(dst) != len(y) {
 | |
| 		panic("floats: lengths of slices do not match")
 | |
| 	}
 | |
| 	for i, val := range s {
 | |
| 		dst[i] = y[i] + alpha*val
 | |
| 	}
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // ApplyFunc applies a function f (math.Exp, math.Sin, etc.) to every element
 | |
| // of the slice s.
 | |
| func Apply(f func(float64) float64, s []float64) {
 | |
| 	for i, val := range s {
 | |
| 		s[i] = f(val)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Count applies the function f to every element of s and returns the number
 | |
| // of times the function returned true.
 | |
| func Count(f func(float64) bool, s []float64) int {
 | |
| 	var n int
 | |
| 	for _, val := range s {
 | |
| 		if f(val) {
 | |
| 			n++
 | |
| 		}
 | |
| 	}
 | |
| 	return n
 | |
| }
 | |
| 
 | |
| // Cumprod finds the cumulative product of the first i elements in
 | |
| // s and puts them in place into the ith element of the
 | |
| // destination. A panic will occur if lengths of do not match.
 | |
| func CumProd(dst, s []float64) []float64 {
 | |
| 	if len(dst) != len(s) {
 | |
| 		panic("floats: length of destination does not match length of the source")
 | |
| 	}
 | |
| 	dst[0] = s[0]
 | |
| 	for i := 1; i < len(s); i++ {
 | |
| 		dst[i] = dst[i-1] * s[i]
 | |
| 	}
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // Cumsum finds the cumulative sum of the first i elements in
 | |
| // s and puts them in place into the ith element of the
 | |
| // destination. A panic will occur if lengths of arguments do not match.
 | |
| func CumSum(dst, s []float64) []float64 {
 | |
| 	if len(dst) != len(s) {
 | |
| 		panic("floats: length of destination does not match length of the source")
 | |
| 	}
 | |
| 	dst[0] = s[0]
 | |
| 	for i := 1; i < len(s); i++ {
 | |
| 		dst[i] = dst[i-1] + s[i]
 | |
| 	}
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // Div performs element-wise division between s
 | |
| // and t and stores the value in s. It panics if the
 | |
| // lengths of s and t are not equal.
 | |
| func Div(s []float64, t []float64) {
 | |
| 	if len(s) != len(t) {
 | |
| 		panic("floats: slice lengths do not match")
 | |
| 	}
 | |
| 	for i, val := range t {
 | |
| 		s[i] /= val
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // DivTo performs element-wise division between s
 | |
| // and t and stores the value in dst. It panics if the
 | |
| // lengths of s, t, and dst are not equal.
 | |
| func DivTo(dst []float64, s []float64, t []float64) []float64 {
 | |
| 	if len(s) != len(t) || len(dst) != len(t) {
 | |
| 		panic("floats: slice lengths do not match")
 | |
| 	}
 | |
| 	for i, val := range t {
 | |
| 		dst[i] = s[i] / val
 | |
| 	}
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // Dot computes the dot product of s1 and s2, i.e.
 | |
| // sum_{i = 1}^N s1[i]*s2[i].
 | |
| // A panic will occur if lengths of arguments do not match.
 | |
| func Dot(s1, s2 []float64) float64 {
 | |
| 	if len(s1) != len(s2) {
 | |
| 		panic("floats: lengths of the slices do not match")
 | |
| 	}
 | |
| 	var sum float64
 | |
| 	for i, val := range s1 {
 | |
| 		sum += val * s2[i]
 | |
| 	}
 | |
| 	return sum
 | |
| }
 | |
| 
 | |
| // Equal returns true if the slices have equal lengths and
 | |
| // all elements are numerically identical.
 | |
| func Equal(s1, s2 []float64) bool {
 | |
| 	if len(s1) != len(s2) {
 | |
| 		return false
 | |
| 	}
 | |
| 	for i, val := range s1 {
 | |
| 		if s2[i] != val {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // EqualsApprox returns true if the slices have equal lengths and
 | |
| // all element pairs have an absolute tolerance less than tol or a
 | |
| // relative tolerance less than tol.
 | |
| func EqualApprox(s1, s2 []float64, tol float64) bool {
 | |
| 	if len(s1) != len(s2) {
 | |
| 		return false
 | |
| 	}
 | |
| 	for i, a := range s1 {
 | |
| 		if !EqualWithinAbsOrRel(a, s2[i], tol, tol) {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // EqualsFunc returns true if the slices have the same lengths
 | |
| // and the function returns true for all element pairs.
 | |
| func EqualFunc(s1, s2 []float64, f func(float64, float64) bool) bool {
 | |
| 	if len(s1) != len(s2) {
 | |
| 		return false
 | |
| 	}
 | |
| 	for i, val := range s1 {
 | |
| 		if !f(val, s2[i]) {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| 
 | |
| }
 | |
| 
 | |
| // ApproxEqualAbs returns true if a and b have an absolute
 | |
| // difference of less than tol.
 | |
| func EqualWithinAbs(a, b, tol float64) bool {
 | |
| 	return a == b || math.Abs(a-b) <= tol
 | |
| }
 | |
| 
 | |
| const minNormalFloat64 = 2.2250738585072014e-308
 | |
| 
 | |
| // ApproxEqualRel returns true if the difference between a and b
 | |
| // is not greater than tol times the greater value.
 | |
| func EqualWithinRel(a, b, tol float64) bool {
 | |
| 	if a == b {
 | |
| 		return true
 | |
| 	}
 | |
| 	delta := math.Abs(a - b)
 | |
| 	if delta <= minNormalFloat64 {
 | |
| 		return delta <= tol*minNormalFloat64
 | |
| 	}
 | |
| 	// We depend on the division in this relationship to identify
 | |
| 	// infinities (we rely on the NaN to fail the test) otherwise
 | |
| 	// we compare Infs of the same sign and evaluate Infs as equal
 | |
| 	// independent of sign.
 | |
| 	return delta/math.Max(math.Abs(a), math.Abs(b)) <= tol
 | |
| }
 | |
| 
 | |
| // EqualsWithinAbsOrRel returns true if a and b are equal to within
 | |
| // the absolute tolerance.
 | |
| func EqualWithinAbsOrRel(a, b, absTol, relTol float64) bool {
 | |
| 	if EqualWithinAbs(a, b, absTol) {
 | |
| 		return true
 | |
| 	}
 | |
| 	return EqualWithinRel(a, b, relTol)
 | |
| }
 | |
| 
 | |
| // EqualWithinULP returns true if a and b are equal to within
 | |
| // the specified number of floating point units in the last place.
 | |
| func EqualWithinULP(a, b float64, ulp uint) bool {
 | |
| 	if a == b {
 | |
| 		return true
 | |
| 	}
 | |
| 	if math.IsNaN(a) || math.IsNaN(b) {
 | |
| 		return false
 | |
| 	}
 | |
| 	if math.Signbit(a) != math.Signbit(b) {
 | |
| 		return math.Float64bits(math.Abs(a))+math.Float64bits(math.Abs(b)) <= uint64(ulp)
 | |
| 	}
 | |
| 	return ulpDiff(math.Float64bits(a), math.Float64bits(b)) <= uint64(ulp)
 | |
| }
 | |
| 
 | |
| func ulpDiff(a, b uint64) uint64 {
 | |
| 	if a > b {
 | |
| 		a, b = b, a
 | |
| 	}
 | |
| 	return b - a
 | |
| }
 | |
| 
 | |
| // Eqlen returns true if all of the slices have equal length,
 | |
| // and false otherwise. Returns true if there are no input slices.
 | |
| func EqualLengths(slices ...[]float64) bool {
 | |
| 	// This length check is needed: http://play.golang.org/p/sdty6YiLhM
 | |
| 	if len(slices) == 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 	l := len(slices[0])
 | |
| 	for i := 1; i < len(slices); i++ {
 | |
| 		if len(slices[i]) != l {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // Find applies f to every element of s and returns the first
 | |
| // k elements for which the f returns true, or all such elements
 | |
| // if k < 0.
 | |
| // Find will reslice inds to have 0 length, and will append
 | |
| // found indices to inds.
 | |
| // If k > 0 and there are fewer than k elements in s satisfying f,
 | |
| // all of the found elements will be returned along with an error.
 | |
| func Find(inds []int, f func(float64) bool, s []float64, k int) ([]int, error) {
 | |
| 
 | |
| 	// inds is also returned to allow for calling with nil
 | |
| 
 | |
| 	// Reslice inds to have zero length
 | |
| 	inds = inds[:0]
 | |
| 
 | |
| 	// If zero elements requested, can just return
 | |
| 	if k == 0 {
 | |
| 		return inds, nil
 | |
| 	}
 | |
| 
 | |
| 	// If k < 0, return all of the found indices
 | |
| 	if k < 0 {
 | |
| 		for i, val := range s {
 | |
| 			if f(val) {
 | |
| 				inds = append(inds, i)
 | |
| 			}
 | |
| 		}
 | |
| 		return inds, nil
 | |
| 	}
 | |
| 
 | |
| 	// Otherwise, find the first k elements
 | |
| 	nFound := 0
 | |
| 	for i, val := range s {
 | |
| 		if f(val) {
 | |
| 			inds = append(inds, i)
 | |
| 			nFound++
 | |
| 			if nFound == k {
 | |
| 				return inds, nil
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	// Finished iterating over the loop, which means k elements were not found
 | |
| 	return inds, errors.New("floats: insufficient elements found")
 | |
| }
 | |
| 
 | |
| // LogSpan returns a set of n equally spaced points in log space between,
 | |
| // l and u where N is equal to len(dst). The first element of the
 | |
| // resulting dst will be l and the final element of dst will be u.
 | |
| // Panics if len(dst) < 2
 | |
| // Note that this call will return NaNs if either l or u are negative, and
 | |
| // will return all zeros if l or u is zero.
 | |
| func LogSpan(dst []float64, l, u float64) []float64 {
 | |
| 	Span(dst, math.Log(l), math.Log(u))
 | |
| 	Apply(math.Exp, dst)
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // Logsumexp returns the log of the sum of the exponentials of the values in s
 | |
| func LogSumExp(s []float64) (lse float64) {
 | |
| 	// Want to do this in a numerically stable way which avoids
 | |
| 	// overflow and underflow
 | |
| 	// First, find the maximum value in the slice.
 | |
| 	maxval, _ := Max(s)
 | |
| 	if math.IsInf(maxval, 0) {
 | |
| 		// If it's infinity either way, the logsumexp will be infinity as well
 | |
| 		// returning now avoids NaNs
 | |
| 		return maxval
 | |
| 	}
 | |
| 	// Compute the sumexp part
 | |
| 	for _, val := range s {
 | |
| 		lse += math.Exp(val - maxval)
 | |
| 	}
 | |
| 	// Take the log and add back on the constant taken out
 | |
| 	return math.Log(lse) + maxval
 | |
| }
 | |
| 
 | |
| // Max returns the maximum value in the slice and the location of
 | |
| // the maximum value. If the input slice is empty, Max will panic.
 | |
| func Max(s []float64) (max float64, ind int) {
 | |
| 	max = s[0]
 | |
| 	ind = 0
 | |
| 	for i, val := range s {
 | |
| 		if val > max {
 | |
| 			max = val
 | |
| 			ind = i
 | |
| 		}
 | |
| 	}
 | |
| 	return max, ind
 | |
| }
 | |
| 
 | |
| // Min returns the minimum value in the slice and the index of
 | |
| // the minimum value. If the input slice is empty, Min will panic.
 | |
| func Min(s []float64) (min float64, ind int) {
 | |
| 	min = s[0]
 | |
| 	ind = 0
 | |
| 	for i, val := range s {
 | |
| 		if val < min {
 | |
| 			min = val
 | |
| 			ind = i
 | |
| 		}
 | |
| 	}
 | |
| 	return min, ind
 | |
| }
 | |
| 
 | |
| // Mul performs element-wise multiplication between s
 | |
| // and t and stores the value in s. Panics if the
 | |
| // lengths of s and t are not equal.
 | |
| func Mul(s []float64, t []float64) {
 | |
| 	if len(s) != len(t) {
 | |
| 		panic("floats: slice lengths do not match")
 | |
| 	}
 | |
| 	for i, val := range t {
 | |
| 		s[i] *= val
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // MulTo performs element-wise multiplication between s
 | |
| // and t and stores the value in dst. Panics if the
 | |
| // lengths of s, t, and dst are not equal.
 | |
| func MulTo(dst []float64, s []float64, t []float64) []float64 {
 | |
| 	if len(s) != len(t) || len(dst) != len(t) {
 | |
| 		panic("floats: slice lengths do not match")
 | |
| 	}
 | |
| 	for i, val := range t {
 | |
| 		dst[i] = val * s[i]
 | |
| 	}
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // Nearest returns the index of the element in s
 | |
| // whose value is nearest to v.  If several such
 | |
| // elements exist, the lowest index is returned.
 | |
| func Nearest(s []float64, v float64) (ind int) {
 | |
| 	dist := math.Abs(v - s[0])
 | |
| 	ind = 0
 | |
| 	for i, val := range s {
 | |
| 		newDist := math.Abs(v - val)
 | |
| 		if newDist < dist {
 | |
| 			dist = newDist
 | |
| 			ind = i
 | |
| 		}
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // NearestInSpan return the index of a hypothetical vector created
 | |
| // by Span with length n and bounds l and u whose value is closest
 | |
| // to v. Assumes u > l. If the value is greater than u or less than
 | |
| // l, the function will panic.
 | |
| func NearestWithinSpan(n int, l, u float64, v float64) int {
 | |
| 	if v < l || v > u {
 | |
| 		panic("floats: value outside span bounds")
 | |
| 	}
 | |
| 
 | |
| 	// Can't guarantee anything about exactly halfway between
 | |
| 	// because of floating point weirdness
 | |
| 	return int((float64(n)-1)/(u-l)*(v-l) + 0.5)
 | |
| }
 | |
| 
 | |
| // Norm returns the L norm of the slice S, defined as
 | |
| // (sum_{i=1}^N s[i]^N)^{1/N}
 | |
| // Special cases:
 | |
| // L = math.Inf(1) gives the maximum value
 | |
| // Does not correctly compute the zero norm (use Count).
 | |
| func Norm(s []float64, L float64) (norm float64) {
 | |
| 	// Should this complain if L is not positive?
 | |
| 	// Should this be done in log space for better numerical stability?
 | |
| 	//	would be more cost
 | |
| 	//	maybe only if L is high?
 | |
| 	if L == 2 {
 | |
| 		for _, val := range s {
 | |
| 			norm += val * val
 | |
| 		}
 | |
| 		return math.Pow(norm, 0.5)
 | |
| 	}
 | |
| 	if L == 1 {
 | |
| 		for _, val := range s {
 | |
| 			norm += math.Abs(val)
 | |
| 		}
 | |
| 		return norm
 | |
| 	}
 | |
| 	if math.IsInf(L, 1) {
 | |
| 		norm, _ = Max(s)
 | |
| 		return norm
 | |
| 	}
 | |
| 	for _, val := range s {
 | |
| 		norm += math.Pow(math.Abs(val), L)
 | |
| 	}
 | |
| 	return math.Pow(norm, 1/L)
 | |
| }
 | |
| 
 | |
| // Prod returns the product of the elements of the slice
 | |
| // Returns 1 if len(s) = 0.
 | |
| func Prod(s []float64) (prod float64) {
 | |
| 	prod = 1
 | |
| 	for _, val := range s {
 | |
| 		prod *= val
 | |
| 	}
 | |
| 	return prod
 | |
| }
 | |
| 
 | |
| // Scale multiplies every element in s by c.
 | |
| func Scale(c float64, s []float64) {
 | |
| 	for i := range s {
 | |
| 		s[i] *= c
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Span returns a set of N equally spaced points between l and u, where N
 | |
| // is equal to the length of the destination. The first element of the destination
 | |
| // is l, the final element of the destination is u.
 | |
| // Panics if len(dst) < 2.
 | |
| func Span(dst []float64, l, u float64) []float64 {
 | |
| 	n := len(dst)
 | |
| 	if n < 2 {
 | |
| 		panic("floats: destination must have length >1")
 | |
| 	}
 | |
| 	step := (u - l) / float64(n-1)
 | |
| 	for i := range dst {
 | |
| 		dst[i] = l + step*float64(i)
 | |
| 	}
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // Sub subtracts, element-wise, the first argument from the second. Assumes
 | |
| // the lengths of s and t match (can be tested with EqLen).
 | |
| func Sub(s, t []float64) {
 | |
| 	if len(s) != len(t) {
 | |
| 		panic("floats: length of the slices do not match")
 | |
| 	}
 | |
| 	for i, val := range t {
 | |
| 		s[i] -= val
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // SubTo subtracts, element-wise, the first argument from the second and
 | |
| // stores the result in dest. Panics if the lengths of s and t do not match.
 | |
| func SubTo(dst, s, t []float64) []float64 {
 | |
| 	if len(s) != len(t) {
 | |
| 		panic("floats: length of subtractor and subtractee do not match")
 | |
| 	}
 | |
| 	if len(dst) != len(s) {
 | |
| 		panic("floats: length of destination does not match length of subtractor")
 | |
| 	}
 | |
| 	for i, val := range t {
 | |
| 		dst[i] = s[i] - val
 | |
| 	}
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // Sum returns the sum of the elements of the slice.
 | |
| func Sum(s []float64) (sum float64) {
 | |
| 	for _, val := range s {
 | |
| 		sum += val
 | |
| 	}
 | |
| 	return
 | |
| }
 | 
