github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/testlapack/dtrexc.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/cmplx"
    10  	"math/rand"
    11  	"testing"
    12  
    13  	"github.com/gonum/blas"
    14  	"github.com/gonum/blas/blas64"
    15  	"github.com/gonum/lapack"
    16  )
    17  
    18  type Dtrexcer interface {
    19  	Dtrexc(compq lapack.EVComp, n int, t []float64, ldt int, q []float64, ldq int, ifst, ilst int, work []float64) (ifstOut, ilstOut int, ok bool)
    20  }
    21  
    22  func DtrexcTest(t *testing.T, impl Dtrexcer) {
    23  	rnd := rand.New(rand.NewSource(1))
    24  
    25  	for _, compq := range []lapack.EVComp{lapack.None, lapack.UpdateSchur} {
    26  		for _, n := range []int{1, 2, 3, 4, 5, 6, 10, 18, 31, 53} {
    27  			for _, extra := range []int{0, 1, 11} {
    28  				for cas := 0; cas < 100; cas++ {
    29  					tmat := randomSchurCanonical(n, n+extra, rnd)
    30  					ifst := rnd.Intn(n)
    31  					ilst := rnd.Intn(n)
    32  					testDtrexc(t, impl, compq, tmat, ifst, ilst, extra, rnd)
    33  				}
    34  			}
    35  		}
    36  	}
    37  
    38  	for _, compq := range []lapack.EVComp{lapack.None, lapack.UpdateSchur} {
    39  		for _, extra := range []int{0, 1, 11} {
    40  			tmat := randomSchurCanonical(0, extra, rnd)
    41  			testDtrexc(t, impl, compq, tmat, 0, 0, extra, rnd)
    42  		}
    43  	}
    44  }
    45  
    46  func testDtrexc(t *testing.T, impl Dtrexcer, compq lapack.EVComp, tmat blas64.General, ifst, ilst, extra int, rnd *rand.Rand) {
    47  	const tol = 1e-13
    48  
    49  	n := tmat.Rows
    50  	fstSize, fstFirst := schurBlockSize(tmat, ifst)
    51  	lstSize, lstFirst := schurBlockSize(tmat, ilst)
    52  
    53  	tmatCopy := cloneGeneral(tmat)
    54  
    55  	var wantq bool
    56  	var q, qCopy blas64.General
    57  	if compq == lapack.UpdateSchur {
    58  		wantq = true
    59  		q = eye(n, n+extra)
    60  		qCopy = cloneGeneral(q)
    61  	}
    62  
    63  	work := nanSlice(n)
    64  
    65  	ifstGot, ilstGot, ok := impl.Dtrexc(compq, n, tmat.Data, tmat.Stride, q.Data, q.Stride, ifst, ilst, work)
    66  
    67  	prefix := fmt.Sprintf("Case compq=%v, n=%v, ifst=%v, nbf=%v, ilst=%v, nbl=%v, extra=%v",
    68  		compq, n, ifst, fstSize, ilst, lstSize, extra)
    69  
    70  	if !generalOutsideAllNaN(tmat) {
    71  		t.Errorf("%v: out-of-range write to T", prefix)
    72  	}
    73  	if wantq && !generalOutsideAllNaN(q) {
    74  		t.Errorf("%v: out-of-range write to Q", prefix)
    75  	}
    76  
    77  	if !ok {
    78  		t.Logf("%v: Dtrexc returned ok=false", prefix)
    79  	}
    80  
    81  	// Check that the index of the first block was correctly updated (if
    82  	// necessary).
    83  	ifstWant := ifst
    84  	if !fstFirst {
    85  		ifstWant = ifst - 1
    86  	}
    87  	if ifstWant != ifstGot {
    88  		t.Errorf("%v: unexpected ifst index. Want %v, got %v ", prefix, ifstWant, ifstGot)
    89  	}
    90  
    91  	// Check that the index of the last block is as expected when ok=true.
    92  	// When ok=false, we don't know at which block the algorithm failed, so
    93  	// we don't check.
    94  	ilstWant := ilst
    95  	if !lstFirst {
    96  		ilstWant--
    97  	}
    98  	if ok {
    99  		if ifstWant < ilstWant {
   100  			// If the blocks are swapped backwards, these
   101  			// adjustments are not necessary, the first row of the
   102  			// last block will end up at ifst.
   103  			switch {
   104  			case fstSize == 2 && lstSize == 1:
   105  				ilstWant--
   106  			case fstSize == 1 && lstSize == 2:
   107  				ilstWant++
   108  			}
   109  		}
   110  		if ilstWant != ilstGot {
   111  			t.Errorf("%v: unexpected ilst index. Want %v, got %v", prefix, ilstWant, ilstGot)
   112  		}
   113  	}
   114  
   115  	if n <= 1 || ifstGot == ilstGot {
   116  		// Too small matrix or no swapping.
   117  		// Check that T was not modified.
   118  		for i := 0; i < n; i++ {
   119  			for j := 0; j < n; j++ {
   120  				if tmat.Data[i*tmat.Stride+j] != tmatCopy.Data[i*tmatCopy.Stride+j] {
   121  					t.Errorf("%v: unexpected modification at T[%v,%v]", prefix, i, j)
   122  				}
   123  			}
   124  		}
   125  		if !wantq {
   126  			return
   127  		}
   128  		// Check that Q was not modified.
   129  		for i := 0; i < n; i++ {
   130  			for j := 0; j < n; j++ {
   131  				if q.Data[i*q.Stride+j] != qCopy.Data[i*qCopy.Stride+j] {
   132  					t.Errorf("%v: unexpected modification at Q[%v,%v]", prefix, i, j)
   133  				}
   134  			}
   135  		}
   136  		return
   137  	}
   138  
   139  	if !isSchurCanonicalGeneral(tmat) {
   140  		t.Errorf("%v: T is not in Schur canonical form", prefix)
   141  	}
   142  
   143  	// Check that T was not modified except above the second subdiagonal in
   144  	// rows and columns [modMin,modMax].
   145  	modMin := min(ifstGot, ilstGot)
   146  	modMax := max(ifstGot, ilstGot) + fstSize
   147  	for i := 0; i < n; i++ {
   148  		for j := 0; j < n; j++ {
   149  			if modMin <= i && i < modMax && j+1 >= i {
   150  				continue
   151  			}
   152  			if modMin <= j && j < modMax && j+1 >= i {
   153  				continue
   154  			}
   155  			diff := tmat.Data[i*tmat.Stride+j] - tmatCopy.Data[i*tmatCopy.Stride+j]
   156  			if diff != 0 {
   157  				t.Errorf("%v: unexpected modification at T[%v,%v]", prefix, i, j)
   158  			}
   159  		}
   160  	}
   161  
   162  	// Check that the block at ifstGot was delivered to ilstGot correctly.
   163  	if fstSize == 1 {
   164  		// 1×1 blocks are swapped exactly.
   165  		got := tmat.Data[ilstGot*tmat.Stride+ilstGot]
   166  		want := tmatCopy.Data[ifstGot*tmatCopy.Stride+ifstGot]
   167  		if want != got {
   168  			t.Errorf("%v: unexpected 1×1 block at T[%v,%v]. Want %v, got %v",
   169  				prefix, want, got, ilstGot, ilstGot)
   170  		}
   171  	} else {
   172  		// Check that the swapped 2×2 block is in Schur canonical form.
   173  		a, b, c, d := extract2x2Block(tmat.Data[ilstGot*tmat.Stride+ilstGot:], tmat.Stride)
   174  		if !isSchurCanonical(a, b, c, d) {
   175  			t.Errorf("%v: 2×2 block at T[%v,%v] not in Schur canonical form", prefix, ilstGot, ilstGot)
   176  		}
   177  		ev1Got, ev2Got := schurBlockEigenvalues(a, b, c, d)
   178  
   179  		// Check that the swapped 2×2 block has the same eigenvalues.
   180  		// The block was originally located at T[ifstGot,ifstGot].
   181  		a, b, c, d = extract2x2Block(tmatCopy.Data[ifstGot*tmatCopy.Stride+ifstGot:], tmatCopy.Stride)
   182  		ev1Want, ev2Want := schurBlockEigenvalues(a, b, c, d)
   183  		if cmplx.Abs(ev1Got-ev1Want) > tol {
   184  			t.Errorf("%v: unexpected first eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v",
   185  				prefix, ilstGot, ilstGot, ev1Want, ev1Got)
   186  		}
   187  		if cmplx.Abs(ev2Got-ev2Want) > tol {
   188  			t.Errorf("%v: unexpected second eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v",
   189  				prefix, ilstGot, ilstGot, ev2Want, ev2Got)
   190  		}
   191  	}
   192  
   193  	if !wantq {
   194  		return
   195  	}
   196  
   197  	if !isOrthonormal(q) {
   198  		t.Errorf("%v: Q is not orthogonal", prefix)
   199  	}
   200  	// Check that Q is unchanged outside of columns [modMin,modMax].
   201  	for i := 0; i < n; i++ {
   202  		for j := 0; j < n; j++ {
   203  			if modMin <= j && j < modMax {
   204  				continue
   205  			}
   206  			if q.Data[i*q.Stride+j]-qCopy.Data[i*qCopy.Stride+j] != 0 {
   207  				t.Errorf("%v: unexpected modification of Q[%v,%v]", prefix, i, j)
   208  			}
   209  		}
   210  	}
   211  	// Check that Q^T TOrig Q == T.
   212  	tq := eye(n, n)
   213  	blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, tmatCopy, q, 0, tq)
   214  	qtq := eye(n, n)
   215  	blas64.Gemm(blas.Trans, blas.NoTrans, 1, q, tq, 0, qtq)
   216  	if !equalApproxGeneral(qtq, tmat, tol) {
   217  		t.Errorf("%v: Q^T (initial T) Q and (final T) are not equal", prefix)
   218  	}
   219  }