gonum.org/v1/gonum@v0.14.0/lapack/testlapack/dlaln2.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  	"testing"
    12  
    13  	"golang.org/x/exp/rand"
    14  )
    15  
    16  type Dlaln2er interface {
    17  	Dlaln2(trans bool, na, nw int, smin, ca float64, a []float64, lda int, d1, d2 float64, b []float64, ldb int, wr, wi float64, x []float64, ldx int) (scale, xnorm float64, ok bool)
    18  }
    19  
    20  func Dlaln2Test(t *testing.T, impl Dlaln2er) {
    21  	rnd := rand.New(rand.NewSource(1))
    22  	for _, trans := range []bool{true, false} {
    23  		for _, na := range []int{1, 2} {
    24  			for _, nw := range []int{1, 2} {
    25  				for _, extra := range []int{0, 1, 2, 13} {
    26  					for cas := 0; cas < 1000; cas++ {
    27  						testDlaln2(t, impl, trans, na, nw, extra, rnd)
    28  					}
    29  				}
    30  			}
    31  		}
    32  	}
    33  }
    34  
    35  func testDlaln2(t *testing.T, impl Dlaln2er, trans bool, na, nw, extra int, rnd *rand.Rand) {
    36  	const tol = 1e-11
    37  
    38  	// Generate random input scalars.
    39  	ca := rnd.NormFloat64()
    40  	d1 := rnd.NormFloat64()
    41  	d2 := rnd.NormFloat64()
    42  
    43  	var w complex128
    44  	if nw == 1 {
    45  		w = complex(rand.NormFloat64(), 0)
    46  	} else {
    47  		w = complex(rand.NormFloat64(), rand.NormFloat64())
    48  	}
    49  	smin := dlamchP * (math.Abs(real(w)) + math.Abs(imag(w)))
    50  
    51  	// Generate random input matrices.
    52  	a := randomGeneral(na, na, na+extra, rnd)
    53  	b := randomGeneral(na, nw, nw+extra, rnd)
    54  	x := randomGeneral(na, nw, nw+extra, rnd)
    55  
    56  	scale, xnormGot, ok := impl.Dlaln2(trans, na, nw, smin, ca, a.Data, a.Stride, d1, d2, b.Data, b.Stride, real(w), imag(w), x.Data, x.Stride)
    57  
    58  	prefix := fmt.Sprintf("Case trans=%t, na=%v, nw=%v, extra=%v", trans, na, nw, extra)
    59  
    60  	if !generalOutsideAllNaN(a) {
    61  		t.Errorf("%v: out-of-range write to A\n%v", prefix, a.Data)
    62  	}
    63  	if !generalOutsideAllNaN(b) {
    64  		t.Errorf("%v: out-of-range write to B\n%v", prefix, b.Data)
    65  	}
    66  	if !generalOutsideAllNaN(x) {
    67  		t.Errorf("%v: out-of-range write to X\n%v", prefix, x.Data)
    68  	}
    69  
    70  	// Scale is documented to be <= 1.
    71  	if scale <= 0 || 1 < scale {
    72  		t.Errorf("%v: invalid value of scale=%v", prefix, scale)
    73  	}
    74  
    75  	// Calculate the infinity norm of X explicitly.
    76  	var xnormWant float64
    77  	for i := 0; i < na; i++ {
    78  		var rowsum float64
    79  		for j := 0; j < nw; j++ {
    80  			rowsum += math.Abs(x.Data[i*x.Stride+j])
    81  		}
    82  		if rowsum > xnormWant {
    83  			xnormWant = rowsum
    84  		}
    85  	}
    86  	if xnormWant != xnormGot {
    87  		t.Errorf("Case %v: unexpected xnorm with scale=%v. Want %v, got %v", prefix, scale, xnormWant, xnormGot)
    88  	}
    89  
    90  	if !ok {
    91  		// If ok is false, the matrix has been perturbed but we don't
    92  		// know how. Return without comparing both sides of the
    93  		// equation.
    94  		return
    95  	}
    96  
    97  	// Compute a complex matrix
    98  	//  M := ca * A - w * D
    99  	// or
   100  	//  M := ca * Aᵀ - w * D.
   101  	m := make([]complex128, na*na)
   102  	if trans {
   103  		// M = ca * Aᵀ
   104  		for i := 0; i < na; i++ {
   105  			for j := 0; j < na; j++ {
   106  				m[i*na+j] = complex(ca*a.Data[j*a.Stride+i], 0)
   107  			}
   108  		}
   109  	} else {
   110  		// M = ca * Aᵀ
   111  		for i := 0; i < na; i++ {
   112  			for j := 0; j < na; j++ {
   113  				m[i*na+j] = complex(ca*a.Data[i*a.Stride+j], 0)
   114  			}
   115  		}
   116  	}
   117  	// Subtract the diagonal matrix w * D.
   118  	m[0] -= w * complex(d1, 0)
   119  	if na == 2 {
   120  		m[3] -= w * complex(d2, 0)
   121  	}
   122  
   123  	// Convert real na×2 matrices X and scale*B into complex na-vectors.
   124  	cx := make([]complex128, na)
   125  	cb := make([]complex128, na)
   126  	switch nw {
   127  	case 1:
   128  		for i := 0; i < na; i++ {
   129  			cx[i] = complex(x.Data[i*x.Stride], 0)
   130  			cb[i] = complex(scale*b.Data[i*x.Stride], 0)
   131  		}
   132  	case 2:
   133  		for i := 0; i < na; i++ {
   134  			cx[i] = complex(x.Data[i*x.Stride], x.Data[i*x.Stride+1])
   135  			cb[i] = complex(scale*b.Data[i*b.Stride], scale*b.Data[i*b.Stride+1])
   136  		}
   137  	}
   138  
   139  	// Compute M * X.
   140  	mx := make([]complex128, na)
   141  	for i := 0; i < na; i++ {
   142  		for j := 0; j < na; j++ {
   143  			mx[i] += m[i*na+j] * cx[j]
   144  		}
   145  	}
   146  	// Check whether |M * X - scale * B|_max <= tol.
   147  	for i := 0; i < na; i++ {
   148  		if cmplx.Abs(mx[i]-cb[i]) > tol {
   149  			t.Errorf("Case %v: unexpected value of left-hand side at row %v with scale=%v. Want %v, got %v", prefix, i, scale, cb[i], mx[i])
   150  		}
   151  	}
   152  }