blob: e433fddfc0ad1ce8b6ba58fff57a0fd1384d6816 [file] [log] [blame]
// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gonum
import (
"math"
"gonum.org/v1/gonum/blas"
"gonum.org/v1/gonum/internal/asm/f64"
)
var _ blas.Float64Level1 = Implementation{}
// Dnrm2 computes the Euclidean norm of a vector,
// sqrt(\sum_i x[i] * x[i]).
// This function returns 0 if incX is negative.
func (Implementation) Dnrm2(n int, x []float64, incX int) float64 {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return 0
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if n < 2 {
if n == 1 {
return math.Abs(x[0])
}
if n == 0 {
return 0
}
if n < 1 {
panic(negativeN)
}
}
var (
scale float64 = 0
sumSquares float64 = 1
)
if incX == 1 {
x = x[:n]
for _, v := range x {
if v == 0 {
continue
}
absxi := math.Abs(v)
if math.IsNaN(absxi) {
return math.NaN()
}
if scale < absxi {
sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
scale = absxi
} else {
sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
}
}
if math.IsInf(scale, 1) {
return math.Inf(1)
}
return scale * math.Sqrt(sumSquares)
}
for ix := 0; ix < n*incX; ix += incX {
val := x[ix]
if val == 0 {
continue
}
absxi := math.Abs(val)
if math.IsNaN(absxi) {
return math.NaN()
}
if scale < absxi {
sumSquares = 1 + sumSquares*(scale/absxi)*(scale/absxi)
scale = absxi
} else {
sumSquares = sumSquares + (absxi/scale)*(absxi/scale)
}
}
if math.IsInf(scale, 1) {
return math.Inf(1)
}
return scale * math.Sqrt(sumSquares)
}
// Dasum computes the sum of the absolute values of the elements of x.
// \sum_i |x[i]|
// Dasum returns 0 if incX is negative.
func (Implementation) Dasum(n int, x []float64, incX int) float64 {
var sum float64
if n < 0 {
panic(negativeN)
}
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return 0
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if incX == 1 {
x = x[:n]
for _, v := range x {
sum += math.Abs(v)
}
return sum
}
for i := 0; i < n; i++ {
sum += math.Abs(x[i*incX])
}
return sum
}
// Idamax returns the index of an element of x with the largest absolute value.
// If there are multiple such indices the earliest is returned.
// Idamax returns -1 if n == 0.
func (Implementation) Idamax(n int, x []float64, incX int) int {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return -1
}
if incX > 0 && (n-1)*incX >= len(x) {
panic(badX)
}
if n < 2 {
if n == 1 {
return 0
}
if n == 0 {
return -1 // Netlib returns invalid index when n == 0
}
if n < 1 {
panic(negativeN)
}
}
idx := 0
max := math.Abs(x[0])
if incX == 1 {
for i, v := range x[:n] {
absV := math.Abs(v)
if absV > max {
max = absV
idx = i
}
}
return idx
}
ix := incX
for i := 1; i < n; i++ {
v := x[ix]
absV := math.Abs(v)
if absV > max {
max = absV
idx = i
}
ix += incX
}
return idx
}
// Dswap exchanges the elements of two vectors.
// x[i], y[i] = y[i], x[i] for all i
func (Implementation) Dswap(n int, x []float64, incX int, y []float64, incY int) {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, v := range x {
x[i], y[i] = y[i], v
}
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
x[ix], y[iy] = y[iy], x[ix]
ix += incX
iy += incY
}
}
// Dcopy copies the elements of x into the elements of y.
// y[i] = x[i] for all i
func (Implementation) Dcopy(n int, x []float64, incX int, y []float64, incY int) {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
copy(y[:n], x[:n])
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
y[iy] = x[ix]
ix += incX
iy += incY
}
}
// Daxpy adds alpha times x to y
// y[i] += alpha * x[i] for all i
func (Implementation) Daxpy(n int, alpha float64, x []float64, incX int, y []float64, incY int) {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if alpha == 0 {
return
}
if incX == 1 && incY == 1 {
f64.AxpyUnitary(alpha, x[:n], y[:n])
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
f64.AxpyInc(alpha, x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))
}
// Drotg computes the plane rotation
// _ _ _ _ _ _
// | c s | | a | | r |
// | -s c | * | b | = | 0 |
// ‾ ‾ ‾ ‾ ‾ ‾
// where
// r = ±√(a^2 + b^2)
// c = a/r, the cosine of the plane rotation
// s = b/r, the sine of the plane rotation
//
// NOTE: There is a discrepancy between the refence implementation and the BLAS
// technical manual regarding the sign for r when a or b are zero.
// Drotg agrees with the definition in the manual and other
// common BLAS implementations.
func (Implementation) Drotg(a, b float64) (c, s, r, z float64) {
if b == 0 && a == 0 {
return 1, 0, a, 0
}
absA := math.Abs(a)
absB := math.Abs(b)
aGTb := absA > absB
r = math.Hypot(a, b)
if aGTb {
r = math.Copysign(r, a)
} else {
r = math.Copysign(r, b)
}
c = a / r
s = b / r
if aGTb {
z = s
} else if c != 0 { // r == 0 case handled above
z = 1 / c
} else {
z = 1
}
return
}
// Drotmg computes the modified Givens rotation. See
// http://www.netlib.org/lapack/explore-html/df/deb/drotmg_8f.html
// for more details.
func (Implementation) Drotmg(d1, d2, x1, y1 float64) (p blas.DrotmParams, rd1, rd2, rx1 float64) {
var p1, p2, q1, q2, u float64
const (
gam = 4096.0
gamsq = 16777216.0
rgamsq = 5.9604645e-8
)
if d1 < 0 {
p.Flag = blas.Rescaling
return
}
p2 = d2 * y1
if p2 == 0 {
p.Flag = blas.Identity
rd1 = d1
rd2 = d2
rx1 = x1
return
}
p1 = d1 * x1
q2 = p2 * y1
q1 = p1 * x1
absQ1 := math.Abs(q1)
absQ2 := math.Abs(q2)
if absQ1 < absQ2 && q2 < 0 {
p.Flag = blas.Rescaling
return
}
if d1 == 0 {
p.Flag = blas.Diagonal
p.H[0] = p1 / p2
p.H[3] = x1 / y1
u = 1 + p.H[0]*p.H[3]
rd1, rd2 = d2/u, d1/u
rx1 = y1 / u
return
}
// Now we know that d1 != 0, and d2 != 0. If d2 == 0, it would be caught
// when p2 == 0, and if d1 == 0, then it is caught above
if absQ1 > absQ2 {
p.H[1] = -y1 / x1
p.H[2] = p2 / p1
u = 1 - p.H[2]*p.H[1]
rd1 = d1
rd2 = d2
rx1 = x1
p.Flag = blas.OffDiagonal
// u must be greater than zero because |q1| > |q2|, so check from netlib
// is unnecessary
// This is left in for ease of comparison with complex routines
//if u > 0 {
rd1 /= u
rd2 /= u
rx1 *= u
//}
} else {
p.Flag = blas.Diagonal
p.H[0] = p1 / p2
p.H[3] = x1 / y1
u = 1 + p.H[0]*p.H[3]
rd1 = d2 / u
rd2 = d1 / u
rx1 = y1 * u
}
for rd1 <= rgamsq || rd1 >= gamsq {
if p.Flag == blas.OffDiagonal {
p.H[0] = 1
p.H[3] = 1
p.Flag = blas.Rescaling
} else if p.Flag == blas.Diagonal {
p.H[1] = -1
p.H[2] = 1
p.Flag = blas.Rescaling
}
if rd1 <= rgamsq {
rd1 *= gam * gam
rx1 /= gam
p.H[0] /= gam
p.H[2] /= gam
} else {
rd1 /= gam * gam
rx1 *= gam
p.H[0] *= gam
p.H[2] *= gam
}
}
for math.Abs(rd2) <= rgamsq || math.Abs(rd2) >= gamsq {
if p.Flag == blas.OffDiagonal {
p.H[0] = 1
p.H[3] = 1
p.Flag = blas.Rescaling
} else if p.Flag == blas.Diagonal {
p.H[1] = -1
p.H[2] = 1
p.Flag = blas.Rescaling
}
if math.Abs(rd2) <= rgamsq {
rd2 *= gam * gam
p.H[1] /= gam
p.H[3] /= gam
} else {
rd2 /= gam * gam
p.H[1] *= gam
p.H[3] *= gam
}
}
return
}
// Drot applies a plane transformation.
// x[i] = c * x[i] + s * y[i]
// y[i] = c * y[i] - s * x[i]
func (Implementation) Drot(n int, x []float64, incX int, y []float64, incY int, c float64, s float64) {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, vx := range x {
vy := y[i]
x[i], y[i] = c*vx+s*vy, c*vy-s*vx
}
return
}
var ix, iy int
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
for i := 0; i < n; i++ {
vx := x[ix]
vy := y[iy]
x[ix], y[iy] = c*vx+s*vy, c*vy-s*vx
ix += incX
iy += incY
}
}
// Drotm applies the modified Givens rotation to the 2×n matrix.
func (Implementation) Drotm(n int, x []float64, incX int, y []float64, incY int, p blas.DrotmParams) {
if incX == 0 {
panic(zeroIncX)
}
if incY == 0 {
panic(zeroIncY)
}
if n <= 0 {
if n == 0 {
return
}
panic(negativeN)
}
if (incX > 0 && (n-1)*incX >= len(x)) || (incX < 0 && (1-n)*incX >= len(x)) {
panic(badX)
}
if (incY > 0 && (n-1)*incY >= len(y)) || (incY < 0 && (1-n)*incY >= len(y)) {
panic(badY)
}
var h11, h12, h21, h22 float64
var ix, iy int
switch p.Flag {
case blas.Identity:
return
case blas.Rescaling:
h11 = p.H[0]
h12 = p.H[2]
h21 = p.H[1]
h22 = p.H[3]
case blas.OffDiagonal:
h11 = 1
h12 = p.H[2]
h21 = p.H[1]
h22 = 1
case blas.Diagonal:
h11 = p.H[0]
h12 = 1
h21 = -1
h22 = p.H[3]
}
if incX < 0 {
ix = (-n + 1) * incX
}
if incY < 0 {
iy = (-n + 1) * incY
}
if incX == 1 && incY == 1 {
x = x[:n]
for i, vx := range x {
vy := y[i]
x[i], y[i] = vx*h11+vy*h12, vx*h21+vy*h22
}
return
}
for i := 0; i < n; i++ {
vx := x[ix]
vy := y[iy]
x[ix], y[iy] = vx*h11+vy*h12, vx*h21+vy*h22
ix += incX
iy += incY
}
}
// Dscal scales x by alpha.
// x[i] *= alpha
// Dscal has no effect if incX < 0.
func (Implementation) Dscal(n int, alpha float64, x []float64, incX int) {
if incX < 1 {
if incX == 0 {
panic(zeroIncX)
}
return
}
if (n-1)*incX >= len(x) {
panic(badX)
}
if n < 1 {
if n == 0 {
return
}
panic(negativeN)
}
if alpha == 0 {
if incX == 1 {
x = x[:n]
for i := range x {
x[i] = 0
}
return
}
for ix := 0; ix < n*incX; ix += incX {
x[ix] = 0
}
return
}
if incX == 1 {
f64.ScalUnitary(alpha, x[:n])
return
}
f64.ScalInc(alpha, x, uintptr(n), uintptr(incX))
}