github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/testlapack/dlatrs.go (about)

     1  // Copyright ©2017 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  	"math"
    10  	"math/rand"
    11  	"testing"
    12  
    13  	"github.com/gonum/blas"
    14  	"github.com/gonum/blas/blas64"
    15  )
    16  
    17  type Dlatrser interface {
    18  	Dlatrs(uplo blas.Uplo, trans blas.Transpose, diag blas.Diag, normin bool, n int, a []float64, lda int, x []float64, cnorm []float64) (scale float64)
    19  }
    20  
    21  func DlatrsTest(t *testing.T, impl Dlatrser) {
    22  	rnd := rand.New(rand.NewSource(1))
    23  	for _, uplo := range []blas.Uplo{blas.Upper, blas.Lower} {
    24  		for _, trans := range []blas.Transpose{blas.Trans, blas.NoTrans} {
    25  			for _, n := range []int{0, 1, 2, 3, 4, 5, 6, 7, 10, 20, 50, 100} {
    26  				for _, lda := range []int{n, 2*n + 1} {
    27  					lda = max(1, lda)
    28  					imats := []int{7, 11, 12, 13, 14, 15, 16, 17, 18}
    29  					if n < 6 {
    30  						imats = append(imats, 19)
    31  					}
    32  					for _, imat := range imats {
    33  						testDlatrs(t, impl, imat, uplo, trans, n, lda, rnd)
    34  					}
    35  				}
    36  			}
    37  		}
    38  	}
    39  }
    40  
    41  func testDlatrs(t *testing.T, impl Dlatrser, imat int, uplo blas.Uplo, trans blas.Transpose, n, lda int, rnd *rand.Rand) {
    42  	const tol = 1e-14
    43  
    44  	a := nanSlice(n * lda)
    45  	b := nanSlice(n)
    46  	work := make([]float64, 3*n)
    47  
    48  	// Generate triangular test matrix and right hand side.
    49  	diag := dlattr(imat, uplo, trans, n, a, lda, b, work, rnd)
    50  	if imat <= 10 {
    51  		// b has not been generated.
    52  		dlarnv(b, 3, rnd)
    53  	}
    54  
    55  	cnorm := nanSlice(n)
    56  	x := make([]float64, n)
    57  
    58  	// Call Dlatrs with normin=false.
    59  	copy(x, b)
    60  	scale := impl.Dlatrs(uplo, trans, diag, false, n, a, lda, x, cnorm)
    61  	prefix := fmt.Sprintf("Case imat=%v (n=%v,lda=%v,trans=%v,uplo=%v,diag=%v", imat, n, lda, trans, uplo, diag)
    62  	for i, v := range cnorm {
    63  		if math.IsNaN(v) {
    64  			t.Errorf("%v: cnorm[%v] not computed (scale=%v,normin=false)", prefix, i, scale)
    65  		}
    66  	}
    67  	resid, hasNaN := dlatrsResidual(uplo, trans, diag, n, a, lda, scale, cnorm, x, b, work[:n])
    68  	if hasNaN {
    69  		t.Errorf("%v: unexpected NaN (scale=%v,normin=false)", prefix, scale)
    70  	} else if resid > tol {
    71  		t.Errorf("%v: residual %v too large (scale=%v,normin=false)", prefix, scale)
    72  	}
    73  
    74  	// Call Dlatrs with normin=true because cnorm has been filled.
    75  	copy(x, b)
    76  	scale = impl.Dlatrs(uplo, trans, diag, true, n, a, lda, x, cnorm)
    77  	resid, hasNaN = dlatrsResidual(uplo, trans, diag, n, a, lda, scale, cnorm, x, b, work[:n])
    78  	if hasNaN {
    79  		t.Errorf("%v: unexpected NaN (scale=%v,normin=true)", prefix, scale)
    80  	} else if resid > tol {
    81  		t.Errorf("%v: residual %v too large (scale=%v,normin=true)", prefix, scale)
    82  	}
    83  }
    84  
    85  // dlatrsResidual returns norm(trans(A)*x-scale*b) / (norm(trans(A))*norm(x)*eps)
    86  // and whether NaN has been encountered in the process.
    87  func dlatrsResidual(uplo blas.Uplo, trans blas.Transpose, diag blas.Diag, n int, a []float64, lda int, scale float64, cnorm []float64, x, b, work []float64) (resid float64, hasNaN bool) {
    88  	if n == 0 {
    89  		return 0, false
    90  	}
    91  
    92  	// Compute the norm of the triangular matrix A using the column norms
    93  	// already computed by Dlatrs.
    94  	var tnorm float64
    95  	if diag == blas.NonUnit {
    96  		for j := 0; j < n; j++ {
    97  			tnorm = math.Max(tnorm, math.Abs(a[j*lda+j])+cnorm[j])
    98  		}
    99  	} else {
   100  		for j := 0; j < n; j++ {
   101  			tnorm = math.Max(tnorm, 1+cnorm[j])
   102  		}
   103  	}
   104  
   105  	eps := dlamchE
   106  	smlnum := dlamchS
   107  	bi := blas64.Implementation()
   108  
   109  	// Compute norm(trans(A)*x-scale*b) / (norm(trans(A))*norm(x)*eps)
   110  	copy(work, x)
   111  	ix := bi.Idamax(n, work, 1)
   112  	xnorm := math.Max(1, math.Abs(work[ix]))
   113  	xscal := 1 / xnorm / float64(n)
   114  	bi.Dscal(n, xscal, work, 1)
   115  	bi.Dtrmv(uplo, trans, diag, n, a, lda, work, 1)
   116  	bi.Daxpy(n, -scale*xscal, b, 1, work, 1)
   117  	for _, v := range work {
   118  		if math.IsNaN(v) {
   119  			return 1 / eps, true
   120  		}
   121  	}
   122  	ix = bi.Idamax(n, work, 1)
   123  	resid = math.Abs(work[ix])
   124  	ix = bi.Idamax(n, x, 1)
   125  	xnorm = math.Abs(x[ix])
   126  	if resid*smlnum <= xnorm {
   127  		if xnorm > 0 {
   128  			resid /= xnorm
   129  		}
   130  	} else if resid > 0 {
   131  		resid = 1 / eps
   132  	}
   133  	if resid*smlnum <= tnorm {
   134  		if tnorm > 0 {
   135  			resid /= tnorm
   136  		}
   137  	} else if resid > 0 {
   138  		resid = 1 / eps
   139  	}
   140  	return resid, false
   141  }