mirror of
https://github.com/gonum/gonum.git
synced 2025-10-04 23:02:42 +08:00

The documentation for DLANTR in Reference-LAPACK says that the input matrix should not be tall if upper triangular and not wide if lower triangular. In most cases providing such "invalid" sizes is harmless and DLANTR works correctly. However, when computing the inf-norm of lower triangular matrices it currently causes an out-of-bound write if the work array is shorter than the number of columns. Even if the reference fixes this, we cannot assume when or if at all other LAPACK implementation providers include it (both OpenBLAS and MKL have this issue, obviously OpenBLAS being much easier to fix). Therefore, the restriction on matrix sizes will have to stay in the reference documentation and we should exlude them from our testing.
147 lines
3.6 KiB
Go
147 lines
3.6 KiB
Go
// 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 testlapack
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"testing"
|
|
|
|
"golang.org/x/exp/rand"
|
|
|
|
"gonum.org/v1/gonum/blas"
|
|
"gonum.org/v1/gonum/floats"
|
|
"gonum.org/v1/gonum/lapack"
|
|
)
|
|
|
|
type Dlantrer interface {
|
|
Dlanger
|
|
Dlantr(norm lapack.MatrixNorm, uplo blas.Uplo, diag blas.Diag, m, n int, a []float64, lda int, work []float64) float64
|
|
}
|
|
|
|
func DlantrTest(t *testing.T, impl Dlantrer) {
|
|
rnd := rand.New(rand.NewSource(1))
|
|
for _, m := range []int{0, 1, 2, 3, 4, 5, 10} {
|
|
for _, n := range []int{0, 1, 2, 3, 4, 5, 10} {
|
|
for _, uplo := range []blas.Uplo{blas.Lower, blas.Upper} {
|
|
if uplo == blas.Upper && m > n {
|
|
continue
|
|
}
|
|
if uplo == blas.Lower && n > m {
|
|
continue
|
|
}
|
|
for _, diag := range []blas.Diag{blas.NonUnit, blas.Unit} {
|
|
for _, lda := range []int{max(1, n), n + 3} {
|
|
dlantrTest(t, impl, rnd, uplo, diag, m, n, lda)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func dlantrTest(t *testing.T, impl Dlantrer, rnd *rand.Rand, uplo blas.Uplo, diag blas.Diag, m, n, lda int) {
|
|
const tol = 1e-14
|
|
|
|
// Generate a random triangular matrix. If the matrix has unit diagonal,
|
|
// don't set the diagonal elements to 1.
|
|
a := make([]float64, max(0, (m-1)*lda+n))
|
|
for i := range a {
|
|
a[i] = rnd.NormFloat64()
|
|
}
|
|
rowsum := make([]float64, m)
|
|
colsum := make([]float64, n)
|
|
var frobWant, maxabsWant float64
|
|
if diag == blas.Unit {
|
|
// Account for the unit diagonal.
|
|
for i := 0; i < min(m, n); i++ {
|
|
rowsum[i] = 1
|
|
colsum[i] = 1
|
|
}
|
|
frobWant = float64(min(m, n))
|
|
if min(m, n) > 0 {
|
|
maxabsWant = 1
|
|
}
|
|
}
|
|
if uplo == blas.Upper {
|
|
for i := 0; i < min(m, n); i++ {
|
|
start := i
|
|
if diag == blas.Unit {
|
|
start = i + 1
|
|
}
|
|
for j := start; j < n; j++ {
|
|
aij := 2*rnd.Float64() - 1
|
|
a[i*lda+j] = aij
|
|
rowsum[i] += math.Abs(aij)
|
|
colsum[j] += math.Abs(aij)
|
|
maxabsWant = math.Max(maxabsWant, math.Abs(aij))
|
|
frobWant += aij * aij
|
|
}
|
|
}
|
|
} else {
|
|
for i := 0; i < m; i++ {
|
|
end := i
|
|
if diag == blas.Unit {
|
|
end = i - 1
|
|
}
|
|
for j := 0; j <= min(end, n-1); j++ {
|
|
aij := 2*rnd.Float64() - 1
|
|
a[i*lda+j] = aij
|
|
rowsum[i] += math.Abs(aij)
|
|
colsum[j] += math.Abs(aij)
|
|
maxabsWant = math.Max(maxabsWant, math.Abs(aij))
|
|
frobWant += aij * aij
|
|
}
|
|
}
|
|
}
|
|
frobWant = math.Sqrt(frobWant)
|
|
var maxcolsumWant, maxrowsumWant float64
|
|
if n > 0 {
|
|
maxcolsumWant = floats.Max(colsum)
|
|
}
|
|
if m > 0 {
|
|
maxrowsumWant = floats.Max(rowsum)
|
|
}
|
|
|
|
aCopy := make([]float64, len(a))
|
|
copy(aCopy, a)
|
|
|
|
for _, norm := range []lapack.MatrixNorm{lapack.MaxAbs, lapack.MaxColumnSum, lapack.MaxRowSum, lapack.Frobenius} {
|
|
name := fmt.Sprintf("norm=%v,uplo=%v,diag=%v,m=%v,n=%v,lda=%v", string(norm), string(uplo), string(diag), m, n, lda)
|
|
|
|
var work []float64
|
|
if norm == lapack.MaxColumnSum {
|
|
work = make([]float64, n)
|
|
}
|
|
normGot := impl.Dlantr(norm, uplo, diag, m, n, a, lda, work)
|
|
|
|
if !floats.Equal(a, aCopy) {
|
|
t.Fatalf("%v: unexpected modification of a", name)
|
|
}
|
|
|
|
if norm == lapack.MaxAbs {
|
|
// MaxAbs norm involves no floating-point computation,
|
|
// so we expect exact equality here.
|
|
if normGot != maxabsWant {
|
|
t.Errorf("%v: unexpected result; got %v, want %v", name, normGot, maxabsWant)
|
|
}
|
|
continue
|
|
}
|
|
|
|
var normWant float64
|
|
switch norm {
|
|
case lapack.MaxColumnSum:
|
|
normWant = maxcolsumWant
|
|
case lapack.MaxRowSum:
|
|
normWant = maxrowsumWant
|
|
case lapack.Frobenius:
|
|
normWant = frobWant
|
|
}
|
|
if math.Abs(normGot-normWant) >= tol {
|
|
t.Errorf("%v: unexpected result; got %v, want %v", name, normGot, normWant)
|
|
}
|
|
}
|
|
}
|