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

     1  // Copyright ©2016 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/rand"
    10  	"testing"
    11  
    12  	"github.com/gonum/blas"
    13  	"github.com/gonum/blas/blas64"
    14  	"github.com/gonum/lapack"
    15  )
    16  
    17  type Dgebaler interface {
    18  	Dgebal(job lapack.Job, n int, a []float64, lda int, scale []float64) (int, int)
    19  }
    20  
    21  func DgebalTest(t *testing.T, impl Dgebaler) {
    22  	rnd := rand.New(rand.NewSource(1))
    23  
    24  	for _, job := range []lapack.Job{lapack.None, lapack.Permute, lapack.Scale, lapack.PermuteScale} {
    25  		for _, n := range []int{0, 1, 2, 3, 4, 5, 6, 10, 18, 31, 53, 100} {
    26  			for _, extra := range []int{0, 11} {
    27  				for cas := 0; cas < 100; cas++ {
    28  					a := unbalancedSparseGeneral(n, n, n+extra, 2*n, rnd)
    29  					testDgebal(t, impl, job, a)
    30  				}
    31  			}
    32  		}
    33  	}
    34  }
    35  
    36  func testDgebal(t *testing.T, impl Dgebaler, job lapack.Job, a blas64.General) {
    37  	const tol = 1e-14
    38  
    39  	n := a.Rows
    40  	extra := a.Stride - n
    41  
    42  	var scale []float64
    43  	if n > 0 {
    44  		scale = nanSlice(n)
    45  	}
    46  
    47  	want := cloneGeneral(a)
    48  
    49  	ilo, ihi := impl.Dgebal(job, n, a.Data, a.Stride, scale)
    50  
    51  	prefix := fmt.Sprintf("Case job=%v, n=%v, extra=%v", job, n, extra)
    52  
    53  	if !generalOutsideAllNaN(a) {
    54  		t.Errorf("%v: out-of-range write to A\n%v", prefix, a.Data)
    55  	}
    56  
    57  	if n == 0 {
    58  		if ilo != 0 {
    59  			t.Errorf("%v: unexpected ilo when n=0. Want 0, got %v", prefix, n, ilo)
    60  		}
    61  		if ihi != -1 {
    62  			t.Errorf("%v: unexpected ihi when n=0. Want -1, got %v", prefix, n, ihi)
    63  		}
    64  		return
    65  	}
    66  
    67  	if job == lapack.None {
    68  		if ilo != 0 {
    69  			t.Errorf("%v: unexpected ilo when job=None. Want 0, got %v", prefix, ilo)
    70  		}
    71  		if ihi != n-1 {
    72  			t.Errorf("%v: unexpected ihi when job=None. Want %v, got %v", prefix, n-1, ihi)
    73  		}
    74  		k := -1
    75  		for i := range scale {
    76  			if scale[i] != 1 {
    77  				k = i
    78  				break
    79  			}
    80  		}
    81  		if k != -1 {
    82  			t.Errorf("%v: unexpected scale[%v] when job=None. Want 1, got %v", prefix, k, scale[k])
    83  		}
    84  		if !equalApproxGeneral(a, want, 0) {
    85  			t.Errorf("%v: unexpected modification of A when job=None", prefix)
    86  		}
    87  		return
    88  	}
    89  
    90  	if ilo < 0 || ihi < ilo || n <= ihi {
    91  		t.Errorf("%v: invalid ordering of ilo=%v and ihi=%v", prefix, ilo, ihi)
    92  	}
    93  
    94  	if ilo >= 2 && !isUpperTriangular(blas64.General{ilo - 1, ilo - 1, a.Stride, a.Data}) {
    95  		t.Errorf("%v: T1 is not upper triangular", prefix)
    96  	}
    97  	m := n - ihi - 1 // Order of T2.
    98  	k := ihi + 1
    99  	if m >= 2 && !isUpperTriangular(blas64.General{m, m, a.Stride, a.Data[k*a.Stride+k:]}) {
   100  		t.Errorf("%v: T2 is not upper triangular", prefix)
   101  	}
   102  
   103  	if job == lapack.Permute || job == lapack.PermuteScale {
   104  		// Check that all rows in [ilo:ihi+1] have at least one nonzero
   105  		// off-diagonal element.
   106  		zeroRow := -1
   107  		for i := ilo; i <= ihi; i++ {
   108  			onlyZeros := true
   109  			for j := ilo; j <= ihi; j++ {
   110  				if i != j && a.Data[i*a.Stride+j] != 0 {
   111  					onlyZeros = false
   112  					break
   113  				}
   114  			}
   115  			if onlyZeros {
   116  				zeroRow = i
   117  				break
   118  			}
   119  		}
   120  		if zeroRow != -1 && ilo != ihi {
   121  			t.Errorf("%v: row %v has only zero off-diagonal elements, ilo=%v, ihi=%v", prefix, zeroRow, ilo, ihi)
   122  		}
   123  		// Check that all columns in [ilo:ihi+1] have at least one nonzero
   124  		// off-diagonal element.
   125  		zeroCol := -1
   126  		for j := ilo; j <= ihi; j++ {
   127  			onlyZeros := true
   128  			for i := ilo; i <= ihi; i++ {
   129  				if i != j && a.Data[i*a.Stride+j] != 0 {
   130  					onlyZeros = false
   131  					break
   132  				}
   133  			}
   134  			if onlyZeros {
   135  				zeroCol = j
   136  				break
   137  			}
   138  		}
   139  		if zeroCol != -1 && ilo != ihi {
   140  			t.Errorf("%v: column %v has only zero off-diagonal elements, ilo=%v, ihi=%v", prefix, zeroCol, ilo, ihi)
   141  		}
   142  
   143  		// Create the permutation matrix P.
   144  		p := eye(n, n)
   145  		for j := n - 1; j > ihi; j-- {
   146  			blas64.Swap(n,
   147  				blas64.Vector{p.Stride, p.Data[j:]},
   148  				blas64.Vector{p.Stride, p.Data[int(scale[j]):]})
   149  		}
   150  		for j := 0; j < ilo; j++ {
   151  			blas64.Swap(n,
   152  				blas64.Vector{p.Stride, p.Data[j:]},
   153  				blas64.Vector{p.Stride, p.Data[int(scale[j]):]})
   154  		}
   155  		// Compute P^T*A*P and store into want.
   156  		ap := zeros(n, n, n)
   157  		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, want, p, 0, ap)
   158  		blas64.Gemm(blas.Trans, blas.NoTrans, 1, p, ap, 0, want)
   159  	}
   160  	if job == lapack.Scale || job == lapack.PermuteScale {
   161  		// Modify want by D and D^{-1}.
   162  		d := eye(n, n)
   163  		dinv := eye(n, n)
   164  		for i := ilo; i <= ihi; i++ {
   165  			d.Data[i*d.Stride+i] = scale[i]
   166  			dinv.Data[i*dinv.Stride+i] = 1 / scale[i]
   167  		}
   168  		ad := zeros(n, n, n)
   169  		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, want, d, 0, ad)
   170  		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, dinv, ad, 0, want)
   171  	}
   172  	if !equalApproxGeneral(want, a, tol) {
   173  		t.Errorf("%v: unexpected value of A, ilo=%v, ihi=%v", prefix, ilo, ihi)
   174  	}
   175  }