// Norm returns the specified (induced) norm of the matrix a. See // https://en.wikipedia.org/wiki/Matrix_norm for the definition of an induced norm. // // Valid norms are: // 1 - The maximum absolute column sum // 2 - Frobenius norm, the square root of the sum of the squares of the elements. // Inf - The maximum absolute row sum. // Norm will panic with ErrNormOrder if an illegal norm order is specified and // with matrix.ErrShape if the matrix has zero size. func Norm(a Matrix, norm float64) float64 { r, c := a.Dims() if r == 0 || c == 0 { panic(matrix.ErrShape) } aU, aTrans := untranspose(a) var work []float64 switch rma := aU.(type) { case RawMatrixer: rm := rma.RawMatrix() n := normLapack(norm, aTrans) if n == lapack.MaxColumnSum { work = make([]float64, rm.Cols) } return lapack64.Lange(n, rm, work) case RawTriangular: rm := rma.RawTriangular() n := normLapack(norm, aTrans) if n == lapack.MaxRowSum || n == lapack.MaxColumnSum { work = make([]float64, rm.N) } return lapack64.Lantr(n, rm, work) case RawSymmetricer: rm := rma.RawSymmetric() n := normLapack(norm, aTrans) if n == lapack.MaxRowSum || n == lapack.MaxColumnSum { work = make([]float64, rm.N) } return lapack64.Lansy(n, rm, work) case *Vector: rv := rma.RawVector() switch norm { default: panic("unreachable") case 1: if aTrans { imax := blas64.Iamax(rma.n, rv) return math.Abs(rma.At(imax, 0)) } return blas64.Asum(rma.n, rv) case 2: return blas64.Nrm2(rma.n, rv) case math.Inf(1): if aTrans { return blas64.Asum(rma.n, rv) } imax := blas64.Iamax(rma.n, rv) return math.Abs(rma.At(imax, 0)) } } switch norm { default: panic("unreachable") case 1: var max float64 for j := 0; j < c; j++ { var sum float64 for i := 0; i < r; i++ { sum += math.Abs(a.At(i, j)) } if sum > max { max = sum } } return max case 2: var sum float64 for i := 0; i < r; i++ { for j := 0; j < c; j++ { v := a.At(i, j) sum += v * v } } return math.Sqrt(sum) case math.Inf(1): var max float64 for i := 0; i < r; i++ { var sum float64 for j := 0; j < c; j++ { sum += math.Abs(a.At(i, j)) } if sum > max { max = sum } } return max } }
func DlangeTest(t *testing.T, impl Dlanger) { for _, test := range []struct { m, n, lda int }{ {4, 3, 0}, {3, 4, 0}, {4, 3, 100}, {3, 4, 100}, } { m := test.m n := test.n lda := test.lda if lda == 0 { lda = n } a := make([]float64, m*lda) for i := range a { a[i] = (rand.Float64() - 0.5) } work := make([]float64, n) for i := range work { work[i] = rand.Float64() } aCopy := make([]float64, len(a)) copy(aCopy, a) // Test MaxAbs norm. norm := impl.Dlange(lapack.MaxAbs, m, n, a, lda, work) var ans float64 for i := 0; i < m; i++ { idx := blas64.Iamax(n, blas64.Vector{1, aCopy[i*lda:]}) ans = math.Max(ans, math.Abs(a[i*lda+idx])) } // Should be strictly equal because there is no floating point summation error. if ans != norm { t.Errorf("MaxAbs mismatch. Want %v, got %v.", ans, norm) } // Test MaxColumnSum norm. norm = impl.Dlange(lapack.MaxColumnSum, m, n, a, lda, work) ans = 0 for i := 0; i < n; i++ { sum := blas64.Asum(m, blas64.Vector{lda, aCopy[i:]}) ans = math.Max(ans, sum) } if math.Abs(norm-ans) > 1e-14 { t.Errorf("MaxColumnSum mismatch. Want %v, got %v.", ans, norm) } // Test MaxRowSum norm. norm = impl.Dlange(lapack.MaxRowSum, m, n, a, lda, work) ans = 0 for i := 0; i < m; i++ { sum := blas64.Asum(n, blas64.Vector{1, aCopy[i*lda:]}) ans = math.Max(ans, sum) } if math.Abs(norm-ans) > 1e-14 { t.Errorf("MaxRowSum mismatch. Want %v, got %v.", ans, norm) } // Test Frobenius norm norm = impl.Dlange(lapack.NormFrob, m, n, a, lda, work) ans = 0 for i := 0; i < m; i++ { sum := blas64.Nrm2(n, blas64.Vector{1, aCopy[i*lda:]}) ans += sum * sum } ans = math.Sqrt(ans) if math.Abs(norm-ans) > 1e-14 { t.Errorf("NormFrob mismatch. Want %v, got %v.", ans, norm) } } }