// Copyright ©2020 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 ( "math" "gonum.org/v1/gonum/blas" "gonum.org/v1/gonum/internal/asm/f64" "gonum.org/v1/gonum/lapack" ) // dlagtm is a local implementation of Dlagtm to keep code paths independent. func dlagtm(trans blas.Transpose, m, n int, alpha float64, dl, d, du []float64, b []float64, ldb int, beta float64, c []float64, ldc int) { if m == 0 || n == 0 { return } if beta != 1 { if beta == 0 { for i := 0; i < m; i++ { ci := c[i*ldc : i*ldc+n] for j := range ci { ci[j] = 0 } } } else { for i := 0; i < m; i++ { ci := c[i*ldc : i*ldc+n] for j := range ci { ci[j] *= beta } } } } if alpha == 0 { return } if m == 1 { if alpha == 1 { for j := 0; j < n; j++ { c[j] += d[0] * b[j] } } else { for j := 0; j < n; j++ { c[j] += alpha * d[0] * b[j] } } return } if trans != blas.NoTrans { dl, du = du, dl } if alpha == 1 { for j := 0; j < n; j++ { c[j] += d[0]*b[j] + du[0]*b[ldb+j] } for i := 1; i < m-1; i++ { for j := 0; j < n; j++ { c[i*ldc+j] += dl[i-1]*b[(i-1)*ldb+j] + d[i]*b[i*ldb+j] + du[i]*b[(i+1)*ldb+j] } } for j := 0; j < n; j++ { c[(m-1)*ldc+j] += dl[m-2]*b[(m-2)*ldb+j] + d[m-1]*b[(m-1)*ldb+j] } } else { for j := 0; j < n; j++ { c[j] += alpha * (d[0]*b[j] + du[0]*b[ldb+j]) } for i := 1; i < m-1; i++ { for j := 0; j < n; j++ { c[i*ldc+j] += alpha * (dl[i-1]*b[(i-1)*ldb+j] + d[i]*b[i*ldb+j] + du[i]*b[(i+1)*ldb+j]) } } for j := 0; j < n; j++ { c[(m-1)*ldc+j] += alpha * (dl[m-2]*b[(m-2)*ldb+j] + d[m-1]*b[(m-1)*ldb+j]) } } } // dlangt is a local implementation of Dlangt to keep code paths independent. func dlangt(norm lapack.MatrixNorm, n int, dl, d, du []float64) float64 { if n == 0 { return 0 } dl = dl[:n-1] d = d[:n] du = du[:n-1] var anorm float64 switch norm { case lapack.MaxAbs: for _, diag := range [][]float64{dl, d, du} { for _, di := range diag { if math.IsNaN(di) { return di } di = math.Abs(di) if di > anorm { anorm = di } } } case lapack.MaxColumnSum: if n == 1 { return math.Abs(d[0]) } anorm = math.Abs(d[0]) + math.Abs(dl[0]) if math.IsNaN(anorm) { return anorm } tmp := math.Abs(du[n-2]) + math.Abs(d[n-1]) if math.IsNaN(tmp) { return tmp } if tmp > anorm { anorm = tmp } for i := 1; i < n-1; i++ { tmp = math.Abs(du[i-1]) + math.Abs(d[i]) + math.Abs(dl[i]) if math.IsNaN(tmp) { return tmp } if tmp > anorm { anorm = tmp } } case lapack.MaxRowSum: if n == 1 { return math.Abs(d[0]) } anorm = math.Abs(d[0]) + math.Abs(du[0]) if math.IsNaN(anorm) { return anorm } tmp := math.Abs(dl[n-2]) + math.Abs(d[n-1]) if math.IsNaN(tmp) { return tmp } if tmp > anorm { anorm = tmp } for i := 1; i < n-1; i++ { tmp = math.Abs(dl[i-1]) + math.Abs(d[i]) + math.Abs(du[i]) if math.IsNaN(tmp) { return tmp } if tmp > anorm { anorm = tmp } } case lapack.Frobenius: panic("not implemented") default: panic("invalid norm") } return anorm } // dlansy is a local implementation of Dlansy to keep code paths independent. func dlansy(norm lapack.MatrixNorm, uplo blas.Uplo, n int, a []float64, lda int) float64 { if n == 0 { return 0 } work := make([]float64, n) switch norm { case lapack.MaxAbs: if uplo == blas.Upper { var max float64 for i := 0; i < n; i++ { for j := i; j < n; j++ { v := math.Abs(a[i*lda+j]) if math.IsNaN(v) { return math.NaN() } if v > max { max = v } } } return max } var max float64 for i := 0; i < n; i++ { for j := 0; j <= i; j++ { v := math.Abs(a[i*lda+j]) if math.IsNaN(v) { return math.NaN() } if v > max { max = v } } } return max case lapack.MaxRowSum, lapack.MaxColumnSum: // A symmetric matrix has the same 1-norm and ∞-norm. for i := 0; i < n; i++ { work[i] = 0 } if uplo == blas.Upper { for i := 0; i < n; i++ { work[i] += math.Abs(a[i*lda+i]) for j := i + 1; j < n; j++ { v := math.Abs(a[i*lda+j]) work[i] += v work[j] += v } } } else { for i := 0; i < n; i++ { for j := 0; j < i; j++ { v := math.Abs(a[i*lda+j]) work[i] += v work[j] += v } work[i] += math.Abs(a[i*lda+i]) } } var max float64 for i := 0; i < n; i++ { v := work[i] if math.IsNaN(v) { return math.NaN() } if v > max { max = v } } return max case lapack.Frobenius: panic("not implemented") default: panic("invalid norm") } } // dlange is a local implementation of Dlange to keep code paths independent. func dlange(norm lapack.MatrixNorm, m, n int, a []float64, lda int) float64 { if m == 0 || n == 0 { return 0 } var value float64 switch norm { case lapack.MaxAbs: for i := 0; i < m; i++ { for j := 0; j < n; j++ { value = math.Max(value, math.Abs(a[i*lda+j])) } } case lapack.MaxColumnSum: work := make([]float64, n) for i := 0; i < m; i++ { for j := 0; j < n; j++ { work[j] += math.Abs(a[i*lda+j]) } } for i := 0; i < n; i++ { value = math.Max(value, work[i]) } case lapack.MaxRowSum: for i := 0; i < m; i++ { var sum float64 for j := 0; j < n; j++ { sum += math.Abs(a[i*lda+j]) } value = math.Max(value, sum) } case lapack.Frobenius: for i := 0; i < m; i++ { row := f64.L2NormUnitary(a[i*lda : i*lda+n]) value = math.Hypot(value, row) } default: panic("invalid norm") } return value } // dlansb is a local implementation of Dlansb to keep code paths independent. func dlansb(norm lapack.MatrixNorm, uplo blas.Uplo, n, kd int, ab []float64, ldab int, work []float64) float64 { if n == 0 { return 0 } var value float64 switch norm { case lapack.MaxAbs: if uplo == blas.Upper { for i := 0; i < n; i++ { for j := 0; j < min(n-i, kd+1); j++ { aij := math.Abs(ab[i*ldab+j]) if aij > value || math.IsNaN(aij) { value = aij } } } } else { for i := 0; i < n; i++ { for j := max(0, kd-i); j < kd+1; j++ { aij := math.Abs(ab[i*ldab+j]) if aij > value || math.IsNaN(aij) { value = aij } } } } case lapack.MaxColumnSum, lapack.MaxRowSum: work = work[:n] var sum float64 if uplo == blas.Upper { for i := range work { work[i] = 0 } for i := 0; i < n; i++ { sum := work[i] + math.Abs(ab[i*ldab]) for j := i + 1; j < min(i+kd+1, n); j++ { aij := math.Abs(ab[i*ldab+j-i]) sum += aij work[j] += aij } if sum > value || math.IsNaN(sum) { value = sum } } } else { for i := 0; i < n; i++ { sum = 0 for j := max(0, i-kd); j < i; j++ { aij := math.Abs(ab[i*ldab+kd+j-i]) sum += aij work[j] += aij } work[i] = sum + math.Abs(ab[i*ldab+kd]) } for _, sum := range work { if sum > value || math.IsNaN(sum) { value = sum } } } case lapack.Frobenius: panic("not implemented") default: panic("invalid norm") } return value } // dlantr is a local implementation of Dlantr to keep code paths independent. func dlantr(norm lapack.MatrixNorm, uplo blas.Uplo, diag blas.Diag, m, n int, a []float64, lda int, work []float64) float64 { // Quick return if possible. minmn := min(m, n) if minmn == 0 { return 0 } switch norm { case lapack.MaxAbs: if diag == blas.Unit { value := 1.0 if uplo == blas.Upper { for i := 0; i < m; i++ { for j := i + 1; j < n; j++ { tmp := math.Abs(a[i*lda+j]) if math.IsNaN(tmp) { return tmp } if tmp > value { value = tmp } } } return value } for i := 1; i < m; i++ { for j := 0; j < min(i, n); j++ { tmp := math.Abs(a[i*lda+j]) if math.IsNaN(tmp) { return tmp } if tmp > value { value = tmp } } } return value } var value float64 if uplo == blas.Upper { for i := 0; i < m; i++ { for j := i; j < n; j++ { tmp := math.Abs(a[i*lda+j]) if math.IsNaN(tmp) { return tmp } if tmp > value { value = tmp } } } return value } for i := 0; i < m; i++ { for j := 0; j <= min(i, n-1); j++ { tmp := math.Abs(a[i*lda+j]) if math.IsNaN(tmp) { return tmp } if tmp > value { value = tmp } } } return value case lapack.MaxColumnSum: if diag == blas.Unit { for i := 0; i < minmn; i++ { work[i] = 1 } for i := minmn; i < n; i++ { work[i] = 0 } if uplo == blas.Upper { for i := 0; i < m; i++ { for j := i + 1; j < n; j++ { work[j] += math.Abs(a[i*lda+j]) } } } else { for i := 1; i < m; i++ { for j := 0; j < min(i, n); j++ { work[j] += math.Abs(a[i*lda+j]) } } } } else { for i := 0; i < n; i++ { work[i] = 0 } if uplo == blas.Upper { for i := 0; i < m; i++ { for j := i; j < n; j++ { work[j] += math.Abs(a[i*lda+j]) } } } else { for i := 0; i < m; i++ { for j := 0; j <= min(i, n-1); j++ { work[j] += math.Abs(a[i*lda+j]) } } } } var max float64 for _, v := range work[:n] { if math.IsNaN(v) { return math.NaN() } if v > max { max = v } } return max case lapack.MaxRowSum: var maxsum float64 if diag == blas.Unit { if uplo == blas.Upper { for i := 0; i < m; i++ { var sum float64 if i < minmn { sum = 1 } for j := i + 1; j < n; j++ { sum += math.Abs(a[i*lda+j]) } if math.IsNaN(sum) { return math.NaN() } if sum > maxsum { maxsum = sum } } return maxsum } else { for i := 0; i < m; i++ { var sum float64 if i < minmn { sum = 1 } for j := 0; j < min(i, n); j++ { sum += math.Abs(a[i*lda+j]) } if math.IsNaN(sum) { return math.NaN() } if sum > maxsum { maxsum = sum } } return maxsum } } else { if uplo == blas.Upper { for i := 0; i < m; i++ { var sum float64 for j := i; j < n; j++ { sum += math.Abs(a[i*lda+j]) } if math.IsNaN(sum) { return sum } if sum > maxsum { maxsum = sum } } return maxsum } else { for i := 0; i < m; i++ { var sum float64 for j := 0; j <= min(i, n-1); j++ { sum += math.Abs(a[i*lda+j]) } if math.IsNaN(sum) { return sum } if sum > maxsum { maxsum = sum } } return maxsum } } case lapack.Frobenius: panic("not implemented") default: panic("invalid norm") } } // dlantb is a local implementation of Dlantb to keep code paths independent. func dlantb(norm lapack.MatrixNorm, uplo blas.Uplo, diag blas.Diag, n, k int, a []float64, lda int, work []float64) float64 { if n == 0 { return 0 } var value float64 switch norm { case lapack.MaxAbs: if uplo == blas.Upper { var jfirst int if diag == blas.Unit { value = 1 jfirst = 1 } for i := 0; i < n; i++ { for _, aij := range a[i*lda+jfirst : i*lda+min(n-i, k+1)] { if math.IsNaN(aij) { return aij } aij = math.Abs(aij) if aij > value { value = aij } } } } else { jlast := k + 1 if diag == blas.Unit { value = 1 jlast = k } for i := 0; i < n; i++ { for _, aij := range a[i*lda+max(0, k-i) : i*lda+jlast] { if math.IsNaN(aij) { return math.NaN() } aij = math.Abs(aij) if aij > value { value = aij } } } } case lapack.MaxRowSum: var sum float64 if uplo == blas.Upper { var jfirst int if diag == blas.Unit { jfirst = 1 } for i := 0; i < n; i++ { sum = 0 if diag == blas.Unit { sum = 1 } for _, aij := range a[i*lda+jfirst : i*lda+min(n-i, k+1)] { sum += math.Abs(aij) } if math.IsNaN(sum) { return math.NaN() } if sum > value { value = sum } } } else { jlast := k + 1 if diag == blas.Unit { jlast = k } for i := 0; i < n; i++ { sum = 0 if diag == blas.Unit { sum = 1 } for _, aij := range a[i*lda+max(0, k-i) : i*lda+jlast] { sum += math.Abs(aij) } if math.IsNaN(sum) { return math.NaN() } if sum > value { value = sum } } } case lapack.MaxColumnSum: work = work[:n] if diag == blas.Unit { for i := range work { work[i] = 1 } } else { for i := range work { work[i] = 0 } } if uplo == blas.Upper { var jfirst int if diag == blas.Unit { jfirst = 1 } for i := 0; i < n; i++ { for j, aij := range a[i*lda+jfirst : i*lda+min(n-i, k+1)] { work[i+jfirst+j] += math.Abs(aij) } } } else { jlast := k + 1 if diag == blas.Unit { jlast = k } for i := 0; i < n; i++ { off := max(0, k-i) for j, aij := range a[i*lda+off : i*lda+jlast] { work[i+j+off-k] += math.Abs(aij) } } } for _, wi := range work { if math.IsNaN(wi) { return math.NaN() } if wi > value { value = wi } } case lapack.Frobenius: panic("not implemented") default: panic("invalid norm") } return value }