gonum.org/v1/gonum@v0.14.0/lapack/testlapack/dgeqp3.go (about) 1 // Copyright ©2015 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package testlapack 6 7 import ( 8 "fmt" 9 "testing" 10 11 "golang.org/x/exp/rand" 12 13 "gonum.org/v1/gonum/blas" 14 "gonum.org/v1/gonum/blas/blas64" 15 "gonum.org/v1/gonum/lapack" 16 ) 17 18 type Dgeqp3er interface { 19 Dlapmter 20 Dgeqp3(m, n int, a []float64, lda int, jpvt []int, tau, work []float64, lwork int) 21 } 22 23 func Dgeqp3Test(t *testing.T, impl Dgeqp3er) { 24 rnd := rand.New(rand.NewSource(1)) 25 for _, m := range []int{0, 1, 2, 3, 4, 5, 12, 23, 129} { 26 for _, n := range []int{0, 1, 2, 3, 4, 5, 12, 23, 129} { 27 for _, lda := range []int{max(1, n), n + 3} { 28 dgeqp3Test(t, impl, rnd, m, n, lda) 29 } 30 } 31 } 32 } 33 34 func dgeqp3Test(t *testing.T, impl Dgeqp3er, rnd *rand.Rand, m, n, lda int) { 35 const ( 36 tol = 1e-14 37 38 all = iota 39 some 40 none 41 ) 42 for _, free := range []int{all, some, none} { 43 name := fmt.Sprintf("m=%d,n=%d,lda=%d,", m, n, lda) 44 45 // Allocate m×n matrix A and fill it with random numbers. 46 a := randomGeneral(m, n, lda, rnd) 47 // Store a copy of A for later comparison. 48 aCopy := cloneGeneral(a) 49 // Allocate a slice of column pivots. 50 jpvt := make([]int, n) 51 for j := range jpvt { 52 switch free { 53 case all: 54 // All columns are free. 55 jpvt[j] = -1 56 name += "free=all" 57 case some: 58 // Some columns are free, some are leading columns. 59 jpvt[j] = rnd.Intn(2) - 1 // -1 or 0 60 name += "free=some" 61 case none: 62 // All columns are leading. 63 jpvt[j] = 0 64 name += "free=none" 65 default: 66 panic("bad freedom") 67 } 68 } 69 // Allocate a slice for scalar factors of elementary 70 // reflectors and fill it with random numbers. Dgeqp3 71 // will overwrite them with valid data. 72 k := min(m, n) 73 tau := make([]float64, k) 74 for i := range tau { 75 tau[i] = rnd.Float64() 76 } 77 // Get optimal workspace size for Dgeqp3. 78 work := make([]float64, 1) 79 impl.Dgeqp3(m, n, a.Data, a.Stride, jpvt, tau, work, -1) 80 lwork := int(work[0]) 81 work = make([]float64, lwork) 82 for i := range work { 83 work[i] = rnd.Float64() 84 } 85 86 // Compute a QR factorization of A with column pivoting. 87 impl.Dgeqp3(m, n, a.Data, a.Stride, jpvt, tau, work, lwork) 88 89 // Compute Q based on the elementary reflectors stored in A. 90 q := constructQ("QR", m, n, a.Data, a.Stride, tau) 91 // Check that Q is orthogonal. 92 if resid := residualOrthogonal(q, false); resid > tol*float64(max(m, n)) { 93 t.Errorf("Case %v: Q not orthogonal; resid=%v, want<=%v", name, resid, tol*float64(max(m, n))) 94 } 95 96 // Copy the upper triangle of A into R. 97 r := zeros(m, n, lda) 98 for i := 0; i < m; i++ { 99 for j := i; j < n; j++ { 100 r.Data[i*r.Stride+j] = a.Data[i*a.Stride+j] 101 } 102 } 103 // Compute Q*R - A*P: 104 // 1. Rearrange the columns of A based on the permutation in jpvt. 105 qrap := cloneGeneral(aCopy) 106 impl.Dlapmt(true, qrap.Rows, qrap.Cols, qrap.Data, qrap.Stride, jpvt) 107 // Compute Q*R - A*P. 108 blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, q, r, -1, qrap) 109 // Check that |Q*R - A*P| is small. 110 resid := dlange(lapack.MaxColumnSum, qrap.Rows, qrap.Cols, qrap.Data, qrap.Stride) 111 if resid > tol*float64(max(m, n)) { 112 t.Errorf("Case %v: |Q*R - A*P|=%v, want<=%v", name, resid, tol*float64(max(m, n))) 113 114 } 115 } 116 }