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