github.com/gonum/lapack@v0.0.0-20181123203213-e4cdc5a0bff9/testlapack/dlaexc.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"
    10  	"math/cmplx"
    11  	"math/rand"
    12  	"testing"
    13  
    14  	"github.com/gonum/blas"
    15  	"github.com/gonum/blas/blas64"
    16  )
    17  
    18  type Dlaexcer interface {
    19  	Dlaexc(wantq bool, n int, t []float64, ldt int, q []float64, ldq int, j1, n1, n2 int, work []float64) bool
    20  }
    21  
    22  func DlaexcTest(t *testing.T, impl Dlaexcer) {
    23  	rnd := rand.New(rand.NewSource(1))
    24  
    25  	for _, wantq := range []bool{true, false} {
    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  					j1 := rnd.Intn(n)
    30  					n1 := min(rnd.Intn(3), n-j1)
    31  					n2 := min(rnd.Intn(3), n-j1-n1)
    32  					testDlaexc(t, impl, wantq, n, j1, n1, n2, extra, rnd)
    33  				}
    34  			}
    35  		}
    36  	}
    37  }
    38  
    39  func testDlaexc(t *testing.T, impl Dlaexcer, wantq bool, n, j1, n1, n2, extra int, rnd *rand.Rand) {
    40  	const tol = 1e-14
    41  
    42  	tmat := randomGeneral(n, n, n+extra, rnd)
    43  	// Zero out the lower triangle.
    44  	for i := 1; i < n; i++ {
    45  		for j := 0; j < i; j++ {
    46  			tmat.Data[i*tmat.Stride+j] = 0
    47  		}
    48  	}
    49  	// Make any 2x2 diagonal block to be in Schur canonical form.
    50  	if n1 == 2 {
    51  		// Diagonal elements equal.
    52  		tmat.Data[(j1+1)*tmat.Stride+j1+1] = tmat.Data[j1*tmat.Stride+j1]
    53  		// Off-diagonal elements of opposite sign.
    54  		c := rnd.NormFloat64()
    55  		if math.Signbit(c) == math.Signbit(tmat.Data[j1*tmat.Stride+j1+1]) {
    56  			c *= -1
    57  		}
    58  		tmat.Data[(j1+1)*tmat.Stride+j1] = c
    59  	}
    60  	if n2 == 2 {
    61  		// Diagonal elements equal.
    62  		tmat.Data[(j1+n1+1)*tmat.Stride+j1+n1+1] = tmat.Data[(j1+n1)*tmat.Stride+j1+n1]
    63  		// Off-diagonal elements of opposite sign.
    64  		c := rnd.NormFloat64()
    65  		if math.Signbit(c) == math.Signbit(tmat.Data[(j1+n1)*tmat.Stride+j1+n1+1]) {
    66  			c *= -1
    67  		}
    68  		tmat.Data[(j1+n1+1)*tmat.Stride+j1+n1] = c
    69  	}
    70  	tmatCopy := cloneGeneral(tmat)
    71  	var q, qCopy blas64.General
    72  	if wantq {
    73  		q = eye(n, n+extra)
    74  		qCopy = cloneGeneral(q)
    75  	}
    76  	work := nanSlice(n)
    77  
    78  	ok := impl.Dlaexc(wantq, n, tmat.Data, tmat.Stride, q.Data, q.Stride, j1, n1, n2, work)
    79  
    80  	prefix := fmt.Sprintf("Case n=%v, j1=%v, n1=%v, n2=%v, wantq=%v, extra=%v", n, j1, n1, n2, wantq, extra)
    81  
    82  	if !generalOutsideAllNaN(tmat) {
    83  		t.Errorf("%v: out-of-range write to T", prefix)
    84  	}
    85  	if wantq && !generalOutsideAllNaN(q) {
    86  		t.Errorf("%v: out-of-range write to Q", prefix)
    87  	}
    88  
    89  	if !ok {
    90  		if n1 == 1 && n2 == 1 {
    91  			t.Errorf("%v: unexpected failure", prefix)
    92  		} else {
    93  			t.Logf("%v: Dlaexc returned false")
    94  		}
    95  	}
    96  
    97  	if !ok || n1 == 0 || n2 == 0 || j1+n1 >= n {
    98  		// Check that T is not modified.
    99  		for i := 0; i < n; i++ {
   100  			for j := 0; j < n; j++ {
   101  				if tmat.Data[i*tmat.Stride+j] != tmatCopy.Data[i*tmatCopy.Stride+j] {
   102  					t.Errorf("%v: ok == false but T[%v,%v] modified", prefix, i, j)
   103  				}
   104  			}
   105  		}
   106  		if !wantq {
   107  			return
   108  		}
   109  		// Check that Q is not modified.
   110  		for i := 0; i < n; i++ {
   111  			for j := 0; j < n; j++ {
   112  				if q.Data[i*q.Stride+j] != qCopy.Data[i*qCopy.Stride+j] {
   113  					t.Errorf("%v: ok == false but Q[%v,%v] modified", prefix, i, j)
   114  				}
   115  			}
   116  		}
   117  		return
   118  	}
   119  
   120  	// Check that T is not modified outside of rows and columns [j1:j1+n1+n2].
   121  	for i := 0; i < n; i++ {
   122  		if j1 <= i && i < j1+n1+n2 {
   123  			continue
   124  		}
   125  		for j := 0; j < n; j++ {
   126  			if j1 <= j && j < j1+n1+n2 {
   127  				continue
   128  			}
   129  			diff := tmat.Data[i*tmat.Stride+j] - tmatCopy.Data[i*tmatCopy.Stride+j]
   130  			if diff != 0 {
   131  				t.Errorf("%v: unexpected modification of T[%v,%v]", prefix, i, j)
   132  			}
   133  		}
   134  	}
   135  
   136  	if n1 == 1 {
   137  		// 1×1 blocks are swapped exactly.
   138  		got := tmat.Data[(j1+n2)*tmat.Stride+j1+n2]
   139  		want := tmatCopy.Data[j1*tmatCopy.Stride+j1]
   140  		if want != got {
   141  			t.Errorf("%v: unexpected value of T[%v,%v]. Want %v, got %v", prefix, j1+n2, j1+n2, want, got)
   142  		}
   143  	} else {
   144  		// Check that the swapped 2×2 block is in Schur canonical form.
   145  		// The n1×n1 block is now located at T[j1+n2,j1+n2].
   146  		a, b, c, d := extract2x2Block(tmat.Data[(j1+n2)*tmat.Stride+j1+n2:], tmat.Stride)
   147  		if !isSchurCanonical(a, b, c, d) {
   148  			t.Errorf("%v: 2×2 block at T[%v,%v] not in Schur canonical form", prefix, j1+n2, j1+n2)
   149  		}
   150  		ev1Got, ev2Got := schurBlockEigenvalues(a, b, c, d)
   151  
   152  		// Check that the swapped 2×2 block has the same eigenvalues.
   153  		// The n1×n1 block was originally located at T[j1,j1].
   154  		a, b, c, d = extract2x2Block(tmatCopy.Data[j1*tmatCopy.Stride+j1:], tmatCopy.Stride)
   155  		ev1Want, ev2Want := schurBlockEigenvalues(a, b, c, d)
   156  		if cmplx.Abs(ev1Got-ev1Want) > tol {
   157  			t.Errorf("%v: unexpected first eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v",
   158  				prefix, j1+n2, j1+n2, ev1Want, ev1Got)
   159  		}
   160  		if cmplx.Abs(ev2Got-ev2Want) > tol {
   161  			t.Errorf("%v: unexpected second eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v",
   162  				prefix, j1+n2, j1+n2, ev2Want, ev2Got)
   163  		}
   164  	}
   165  	if n2 == 1 {
   166  		// 1×1 blocks are swapped exactly.
   167  		got := tmat.Data[j1*tmat.Stride+j1]
   168  		want := tmatCopy.Data[(j1+n1)*tmatCopy.Stride+j1+n1]
   169  		if want != got {
   170  			t.Errorf("%v: unexpected value of T[%v,%v]. Want %v, got %v", prefix, j1, j1, want, got)
   171  		}
   172  	} else {
   173  		// Check that the swapped 2×2 block is in Schur canonical form.
   174  		// The n2×n2 block is now located at T[j1,j1].
   175  		a, b, c, d := extract2x2Block(tmat.Data[j1*tmat.Stride+j1:], tmat.Stride)
   176  		if !isSchurCanonical(a, b, c, d) {
   177  			t.Errorf("%v: 2×2 block at T[%v,%v] not in Schur canonical form", prefix, j1, j1)
   178  		}
   179  		ev1Got, ev2Got := schurBlockEigenvalues(a, b, c, d)
   180  
   181  		// Check that the swapped 2×2 block has the same eigenvalues.
   182  		// The n2×n2 block was originally located at T[j1+n1,j1+n1].
   183  		a, b, c, d = extract2x2Block(tmatCopy.Data[(j1+n1)*tmatCopy.Stride+j1+n1:], tmatCopy.Stride)
   184  		ev1Want, ev2Want := schurBlockEigenvalues(a, b, c, d)
   185  		if cmplx.Abs(ev1Got-ev1Want) > tol {
   186  			t.Errorf("%v: unexpected first eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v",
   187  				prefix, j1, j1, ev1Want, ev1Got)
   188  		}
   189  		if cmplx.Abs(ev2Got-ev2Want) > tol {
   190  			t.Errorf("%v: unexpected second eigenvalue of 2×2 block at T[%v,%v]. Want %v, got %v",
   191  				prefix, j1, j1, ev2Want, ev2Got)
   192  		}
   193  	}
   194  
   195  	if !wantq {
   196  		return
   197  	}
   198  
   199  	if !isOrthonormal(q) {
   200  		t.Errorf("%v: Q is not orthogonal", prefix)
   201  	}
   202  	// Check that Q is unchanged outside of columns [j1:j1+n1+n2].
   203  	for i := 0; i < n; i++ {
   204  		for j := 0; j < n; j++ {
   205  			if j1 <= j && j < j1+n1+n2 {
   206  				continue
   207  			}
   208  			diff := q.Data[i*q.Stride+j] - qCopy.Data[i*qCopy.Stride+j]
   209  			if diff != 0 {
   210  				t.Errorf("%v: unexpected modification of Q[%v,%v]", prefix, i, j)
   211  			}
   212  		}
   213  	}
   214  	// Check that Q^T TOrig Q == T.
   215  	tq := eye(n, n)
   216  	blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, tmatCopy, q, 0, tq)
   217  	qtq := eye(n, n)
   218  	blas64.Gemm(blas.Trans, blas.NoTrans, 1, q, tq, 0, qtq)
   219  	for i := 0; i < n; i++ {
   220  		for j := 0; j < n; j++ {
   221  			diff := qtq.Data[i*qtq.Stride+j] - tmat.Data[i*tmat.Stride+j]
   222  			if math.Abs(diff) > tol {
   223  				t.Errorf("%v: unexpected value of T[%v,%v]", prefix, i, j)
   224  			}
   225  		}
   226  	}
   227  }