gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/lapack/testlapack/general.go (about)

     1  // Copyright ©2015 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  	"gonum.org/v1/gonum/blas"
    16  	"gonum.org/v1/gonum/blas/blas64"
    17  	"gonum.org/v1/gonum/lapack"
    18  )
    19  
    20  const (
    21  	// dlamchE is the machine epsilon. For IEEE this is 2^{-53}.
    22  	dlamchE = 0x1p-53
    23  	dlamchB = 2
    24  	dlamchP = dlamchB * dlamchE
    25  	// dlamchS is the smallest normal number. For IEEE this is 2^{-1022}.
    26  	dlamchS = 0x1p-1022
    27  
    28  	safmin = dlamchS
    29  	safmax = 1 / safmin
    30  	ulp    = dlamchP
    31  	smlnum = safmin / ulp
    32  	bignum = safmax * ulp
    33  )
    34  
    35  // worklen describes how much workspace a test should use.
    36  type worklen int
    37  
    38  const (
    39  	minimumWork worklen = iota
    40  	mediumWork
    41  	optimumWork
    42  )
    43  
    44  func (wl worklen) String() string {
    45  	switch wl {
    46  	case minimumWork:
    47  		return "minimum"
    48  	case mediumWork:
    49  		return "medium"
    50  	case optimumWork:
    51  		return "optimum"
    52  	}
    53  	return ""
    54  }
    55  
    56  func normToString(norm lapack.MatrixNorm) string {
    57  	switch norm {
    58  	case lapack.MaxAbs:
    59  		return "MaxAbs"
    60  	case lapack.MaxRowSum:
    61  		return "MaxRowSum"
    62  	case lapack.MaxColumnSum:
    63  		return "MaxColSum"
    64  	case lapack.Frobenius:
    65  		return "Frobenius"
    66  	default:
    67  		panic("invalid norm")
    68  	}
    69  }
    70  
    71  func uploToString(uplo blas.Uplo) string {
    72  	switch uplo {
    73  	case blas.Lower:
    74  		return "Lower"
    75  	case blas.Upper:
    76  		return "Upper"
    77  	default:
    78  		panic("invalid uplo")
    79  	}
    80  }
    81  
    82  func diagToString(diag blas.Diag) string {
    83  	switch diag {
    84  	case blas.NonUnit:
    85  		return "NonUnit"
    86  	case blas.Unit:
    87  		return "Unit"
    88  	default:
    89  		panic("invalid diag")
    90  	}
    91  }
    92  
    93  func sideToString(side blas.Side) string {
    94  	switch side {
    95  	case blas.Left:
    96  		return "Left"
    97  	case blas.Right:
    98  		return "Right"
    99  	default:
   100  		panic("invalid side")
   101  	}
   102  }
   103  
   104  func transToString(trans blas.Transpose) string {
   105  	switch trans {
   106  	case blas.NoTrans:
   107  		return "NoTrans"
   108  	case blas.Trans:
   109  		return "Trans"
   110  	case blas.ConjTrans:
   111  		return "ConjTrans"
   112  	default:
   113  		panic("invalid trans")
   114  	}
   115  }
   116  
   117  // nanSlice allocates a new slice of length n filled with NaN.
   118  func nanSlice(n int) []float64 {
   119  	s := make([]float64, n)
   120  	for i := range s {
   121  		s[i] = math.NaN()
   122  	}
   123  	return s
   124  }
   125  
   126  // randomSlice allocates a new slice of length n filled with random values.
   127  func randomSlice(n int, rnd *rand.Rand) []float64 {
   128  	s := make([]float64, n)
   129  	for i := range s {
   130  		s[i] = rnd.NormFloat64()
   131  	}
   132  	return s
   133  }
   134  
   135  // nanGeneral allocates a new r×c general matrix filled with NaN values.
   136  func nanGeneral(r, c, stride int) blas64.General {
   137  	if r < 0 || c < 0 {
   138  		panic("bad matrix size")
   139  	}
   140  	if r == 0 || c == 0 {
   141  		return blas64.General{Stride: max(1, stride)}
   142  	}
   143  	if stride < c {
   144  		panic("bad stride")
   145  	}
   146  	return blas64.General{
   147  		Rows:   r,
   148  		Cols:   c,
   149  		Stride: stride,
   150  		Data:   nanSlice((r-1)*stride + c),
   151  	}
   152  }
   153  
   154  // randomGeneral allocates a new r×c general matrix filled with random
   155  // numbers. Out-of-range elements are filled with NaN values.
   156  func randomGeneral(r, c, stride int, rnd *rand.Rand) blas64.General {
   157  	ans := nanGeneral(r, c, stride)
   158  	for i := 0; i < r; i++ {
   159  		for j := 0; j < c; j++ {
   160  			ans.Data[i*ans.Stride+j] = rnd.NormFloat64()
   161  		}
   162  	}
   163  	return ans
   164  }
   165  
   166  // randomHessenberg allocates a new n×n Hessenberg matrix filled with zeros
   167  // under the first subdiagonal and with random numbers elsewhere. Out-of-range
   168  // elements are filled with NaN values.
   169  func randomHessenberg(n, stride int, rnd *rand.Rand) blas64.General {
   170  	ans := nanGeneral(n, n, stride)
   171  	for i := 0; i < n; i++ {
   172  		for j := 0; j < i-1; j++ {
   173  			ans.Data[i*ans.Stride+j] = 0
   174  		}
   175  		for j := max(0, i-1); j < n; j++ {
   176  			ans.Data[i*ans.Stride+j] = rnd.NormFloat64()
   177  		}
   178  	}
   179  	return ans
   180  }
   181  
   182  // randomSchurCanonical returns a random, general matrix in Schur canonical
   183  // form, that is, block upper triangular with 1×1 and 2×2 diagonal blocks where
   184  // each 2×2 diagonal block has its diagonal elements equal and its off-diagonal
   185  // elements of opposite sign. bad controls whether the returned matrix will have
   186  // zero or tiny eigenvalues.
   187  func randomSchurCanonical(n, stride int, bad bool, rnd *rand.Rand) (t blas64.General, wr, wi []float64) {
   188  	t = randomGeneral(n, n, stride, rnd)
   189  	// Zero out the lower triangle including the diagonal which will be set later.
   190  	for i := 0; i < t.Rows; i++ {
   191  		for j := 0; j <= i; j++ {
   192  			t.Data[i*t.Stride+j] = 0
   193  		}
   194  	}
   195  	// Randomly create 2×2 diagonal blocks.
   196  	for i := 0; i < t.Rows; {
   197  		a := rnd.NormFloat64()
   198  		if bad && rnd.Float64() < 0.5 {
   199  			if rnd.Float64() < 0.5 {
   200  				// A quarter of real parts of eigenvalues will be tiny.
   201  				a = dlamchS
   202  			} else {
   203  				// A quarter of them will be zero.
   204  				a = 0
   205  			}
   206  		}
   207  
   208  		// A half of eigenvalues will be real.
   209  		if rnd.Float64() < 0.5 || i == t.Rows-1 {
   210  			// Store 1×1 block at the diagonal of T.
   211  			t.Data[i*t.Stride+i] = a
   212  			wr = append(wr, a)
   213  			wi = append(wi, 0)
   214  			i++
   215  			continue
   216  		}
   217  
   218  		// Diagonal elements are equal.
   219  		d := a
   220  		// Element under the diagonal is "normal".
   221  		c := rnd.NormFloat64()
   222  		// Element above the diagonal cannot be zero.
   223  		var b float64
   224  		if bad && rnd.Float64() < 0.5 {
   225  			b = dlamchS
   226  		} else {
   227  			b = rnd.NormFloat64()
   228  		}
   229  		// Make sure off-diagonal elements are of opposite sign.
   230  		if math.Signbit(b) == math.Signbit(c) {
   231  			c *= -1
   232  		}
   233  
   234  		// Store 2×2 block at the diagonal of T.
   235  		t.Data[i*t.Stride+i], t.Data[i*t.Stride+i+1] = a, b
   236  		t.Data[(i+1)*t.Stride+i], t.Data[(i+1)*t.Stride+i+1] = c, d
   237  
   238  		wr = append(wr, a, a)
   239  		im := math.Sqrt(math.Abs(b)) * math.Sqrt(math.Abs(c))
   240  		wi = append(wi, im, -im)
   241  		i += 2
   242  	}
   243  	return t, wr, wi
   244  }
   245  
   246  // blockedUpperTriGeneral returns a normal random, general matrix in the form
   247  //
   248  //	          c-k-l  k    l
   249  //	A =    k [  0   A12  A13 ] if r-k-l >= 0;
   250  //	       l [  0    0   A23 ]
   251  //	   r-k-l [  0    0    0  ]
   252  //
   253  //	        c-k-l  k    l
   254  //	A =  k [  0   A12  A13 ] if r-k-l < 0;
   255  //	   r-k [  0    0   A23 ]
   256  //
   257  // where the k×k matrix A12 and l×l matrix is non-singular
   258  // upper triangular. A23 is l×l upper triangular if r-k-l >= 0,
   259  // otherwise A23 is (r-k)×l upper trapezoidal.
   260  func blockedUpperTriGeneral(r, c, k, l, stride int, kblock bool, rnd *rand.Rand) blas64.General {
   261  	t := l
   262  	if kblock {
   263  		t += k
   264  	}
   265  	ans := zeros(r, c, stride)
   266  	for i := 0; i < min(r, t); i++ {
   267  		var v float64
   268  		for v == 0 {
   269  			v = rnd.NormFloat64()
   270  		}
   271  		ans.Data[i*ans.Stride+i+(c-t)] = v
   272  	}
   273  	for i := 0; i < min(r, t); i++ {
   274  		for j := i + (c - t) + 1; j < c; j++ {
   275  			ans.Data[i*ans.Stride+j] = rnd.NormFloat64()
   276  		}
   277  	}
   278  	return ans
   279  }
   280  
   281  // nanTriangular allocates a new r×c triangular matrix filled with NaN values.
   282  func nanTriangular(uplo blas.Uplo, n, stride int) blas64.Triangular {
   283  	if n < 0 {
   284  		panic("bad matrix size")
   285  	}
   286  	if n == 0 {
   287  		return blas64.Triangular{
   288  			Stride: max(1, stride),
   289  			Uplo:   uplo,
   290  			Diag:   blas.NonUnit,
   291  		}
   292  	}
   293  	if stride < n {
   294  		panic("bad stride")
   295  	}
   296  	return blas64.Triangular{
   297  		N:      n,
   298  		Stride: stride,
   299  		Data:   nanSlice((n-1)*stride + n),
   300  		Uplo:   uplo,
   301  		Diag:   blas.NonUnit,
   302  	}
   303  }
   304  
   305  // generalOutsideAllNaN returns whether all out-of-range elements have NaN
   306  // values.
   307  func generalOutsideAllNaN(a blas64.General) bool {
   308  	// Check after last column.
   309  	for i := 0; i < a.Rows-1; i++ {
   310  		for _, v := range a.Data[i*a.Stride+a.Cols : i*a.Stride+a.Stride] {
   311  			if !math.IsNaN(v) {
   312  				return false
   313  			}
   314  		}
   315  	}
   316  	// Check after last element.
   317  	last := (a.Rows-1)*a.Stride + a.Cols
   318  	if a.Rows == 0 || a.Cols == 0 {
   319  		last = 0
   320  	}
   321  	for _, v := range a.Data[last:] {
   322  		if !math.IsNaN(v) {
   323  			return false
   324  		}
   325  	}
   326  	return true
   327  }
   328  
   329  // triangularOutsideAllNaN returns whether all out-of-triangle elements have NaN
   330  // values.
   331  func triangularOutsideAllNaN(a blas64.Triangular) bool {
   332  	if a.Uplo == blas.Upper {
   333  		// Check below diagonal.
   334  		for i := 0; i < a.N; i++ {
   335  			for _, v := range a.Data[i*a.Stride : i*a.Stride+i] {
   336  				if !math.IsNaN(v) {
   337  					return false
   338  				}
   339  			}
   340  		}
   341  		// Check after last column.
   342  		for i := 0; i < a.N-1; i++ {
   343  			for _, v := range a.Data[i*a.Stride+a.N : i*a.Stride+a.Stride] {
   344  				if !math.IsNaN(v) {
   345  					return false
   346  				}
   347  			}
   348  		}
   349  	} else {
   350  		// Check above diagonal.
   351  		for i := 0; i < a.N-1; i++ {
   352  			for _, v := range a.Data[i*a.Stride+i+1 : i*a.Stride+a.Stride] {
   353  				if !math.IsNaN(v) {
   354  					return false
   355  				}
   356  			}
   357  		}
   358  	}
   359  	// Check after last element.
   360  	for _, v := range a.Data[max(0, a.N-1)*a.Stride+a.N:] {
   361  		if !math.IsNaN(v) {
   362  			return false
   363  		}
   364  	}
   365  	return true
   366  }
   367  
   368  // transposeGeneral returns a new general matrix that is the transpose of the
   369  // input. Nothing is done with data outside the {rows, cols} limit of the general.
   370  func transposeGeneral(a blas64.General) blas64.General {
   371  	ans := blas64.General{
   372  		Rows:   a.Cols,
   373  		Cols:   a.Rows,
   374  		Stride: a.Rows,
   375  		Data:   make([]float64, a.Cols*a.Rows),
   376  	}
   377  	for i := 0; i < a.Rows; i++ {
   378  		for j := 0; j < a.Cols; j++ {
   379  			ans.Data[j*ans.Stride+i] = a.Data[i*a.Stride+j]
   380  		}
   381  	}
   382  	return ans
   383  }
   384  
   385  // columnNorms returns the column norms of a.
   386  func columnNorms(m, n int, a []float64, lda int) []float64 {
   387  	bi := blas64.Implementation()
   388  	norms := make([]float64, n)
   389  	for j := 0; j < n; j++ {
   390  		norms[j] = bi.Dnrm2(m, a[j:], lda)
   391  	}
   392  	return norms
   393  }
   394  
   395  // extractVMat collects the single reflectors from a into a matrix.
   396  func extractVMat(m, n int, a []float64, lda int, direct lapack.Direct, store lapack.StoreV) blas64.General {
   397  	k := min(m, n)
   398  	switch {
   399  	default:
   400  		panic("not implemented")
   401  	case direct == lapack.Forward && store == lapack.ColumnWise:
   402  		v := blas64.General{
   403  			Rows:   m,
   404  			Cols:   k,
   405  			Stride: k,
   406  			Data:   make([]float64, m*k),
   407  		}
   408  		for i := 0; i < k; i++ {
   409  			for j := 0; j < i; j++ {
   410  				v.Data[j*v.Stride+i] = 0
   411  			}
   412  			v.Data[i*v.Stride+i] = 1
   413  			for j := i + 1; j < m; j++ {
   414  				v.Data[j*v.Stride+i] = a[j*lda+i]
   415  			}
   416  		}
   417  		return v
   418  	case direct == lapack.Forward && store == lapack.RowWise:
   419  		v := blas64.General{
   420  			Rows:   k,
   421  			Cols:   n,
   422  			Stride: n,
   423  			Data:   make([]float64, k*n),
   424  		}
   425  		for i := 0; i < k; i++ {
   426  			for j := 0; j < i; j++ {
   427  				v.Data[i*v.Stride+j] = 0
   428  			}
   429  			v.Data[i*v.Stride+i] = 1
   430  			for j := i + 1; j < n; j++ {
   431  				v.Data[i*v.Stride+j] = a[i*lda+j]
   432  			}
   433  		}
   434  		return v
   435  	}
   436  }
   437  
   438  // constructBidiagonal constructs a bidiagonal matrix with the given diagonal
   439  // and off-diagonal elements.
   440  func constructBidiagonal(uplo blas.Uplo, n int, d, e []float64) blas64.General {
   441  	bMat := blas64.General{
   442  		Rows:   n,
   443  		Cols:   n,
   444  		Stride: n,
   445  		Data:   make([]float64, n*n),
   446  	}
   447  
   448  	for i := 0; i < n-1; i++ {
   449  		bMat.Data[i*bMat.Stride+i] = d[i]
   450  		if uplo == blas.Upper {
   451  			bMat.Data[i*bMat.Stride+i+1] = e[i]
   452  		} else {
   453  			bMat.Data[(i+1)*bMat.Stride+i] = e[i]
   454  		}
   455  	}
   456  	bMat.Data[(n-1)*bMat.Stride+n-1] = d[n-1]
   457  	return bMat
   458  }
   459  
   460  // constructVMat transforms the v matrix based on the storage.
   461  func constructVMat(vMat blas64.General, store lapack.StoreV, direct lapack.Direct) blas64.General {
   462  	m := vMat.Rows
   463  	k := vMat.Cols
   464  	switch {
   465  	default:
   466  		panic("not implemented")
   467  	case store == lapack.ColumnWise && direct == lapack.Forward:
   468  		ldv := k
   469  		v := make([]float64, m*k)
   470  		for i := 0; i < m; i++ {
   471  			for j := 0; j < k; j++ {
   472  				if j > i {
   473  					v[i*ldv+j] = 0
   474  				} else if j == i {
   475  					v[i*ldv+i] = 1
   476  				} else {
   477  					v[i*ldv+j] = vMat.Data[i*vMat.Stride+j]
   478  				}
   479  			}
   480  		}
   481  		return blas64.General{
   482  			Rows:   m,
   483  			Cols:   k,
   484  			Stride: k,
   485  			Data:   v,
   486  		}
   487  	case store == lapack.RowWise && direct == lapack.Forward:
   488  		ldv := m
   489  		v := make([]float64, m*k)
   490  		for i := 0; i < m; i++ {
   491  			for j := 0; j < k; j++ {
   492  				if j > i {
   493  					v[j*ldv+i] = 0
   494  				} else if j == i {
   495  					v[j*ldv+i] = 1
   496  				} else {
   497  					v[j*ldv+i] = vMat.Data[i*vMat.Stride+j]
   498  				}
   499  			}
   500  		}
   501  		return blas64.General{
   502  			Rows:   k,
   503  			Cols:   m,
   504  			Stride: m,
   505  			Data:   v,
   506  		}
   507  	case store == lapack.ColumnWise && direct == lapack.Backward:
   508  		rowsv := m
   509  		ldv := k
   510  		v := make([]float64, m*k)
   511  		for i := 0; i < m; i++ {
   512  			for j := 0; j < k; j++ {
   513  				vrow := rowsv - i - 1
   514  				vcol := k - j - 1
   515  				if j > i {
   516  					v[vrow*ldv+vcol] = 0
   517  				} else if j == i {
   518  					v[vrow*ldv+vcol] = 1
   519  				} else {
   520  					v[vrow*ldv+vcol] = vMat.Data[i*vMat.Stride+j]
   521  				}
   522  			}
   523  		}
   524  		return blas64.General{
   525  			Rows:   rowsv,
   526  			Cols:   ldv,
   527  			Stride: ldv,
   528  			Data:   v,
   529  		}
   530  	case store == lapack.RowWise && direct == lapack.Backward:
   531  		rowsv := k
   532  		ldv := m
   533  		v := make([]float64, m*k)
   534  		for i := 0; i < m; i++ {
   535  			for j := 0; j < k; j++ {
   536  				vcol := ldv - i - 1
   537  				vrow := k - j - 1
   538  				if j > i {
   539  					v[vrow*ldv+vcol] = 0
   540  				} else if j == i {
   541  					v[vrow*ldv+vcol] = 1
   542  				} else {
   543  					v[vrow*ldv+vcol] = vMat.Data[i*vMat.Stride+j]
   544  				}
   545  			}
   546  		}
   547  		return blas64.General{
   548  			Rows:   rowsv,
   549  			Cols:   ldv,
   550  			Stride: ldv,
   551  			Data:   v,
   552  		}
   553  	}
   554  }
   555  
   556  func constructH(tau []float64, v blas64.General, store lapack.StoreV, direct lapack.Direct) blas64.General {
   557  	m := v.Rows
   558  	k := v.Cols
   559  	if store == lapack.RowWise {
   560  		m, k = k, m
   561  	}
   562  	h := blas64.General{
   563  		Rows:   m,
   564  		Cols:   m,
   565  		Stride: m,
   566  		Data:   make([]float64, m*m),
   567  	}
   568  	for i := 0; i < m; i++ {
   569  		h.Data[i*m+i] = 1
   570  	}
   571  	for i := 0; i < k; i++ {
   572  		vecData := make([]float64, m)
   573  		if store == lapack.ColumnWise {
   574  			for j := 0; j < m; j++ {
   575  				vecData[j] = v.Data[j*v.Cols+i]
   576  			}
   577  		} else {
   578  			for j := 0; j < m; j++ {
   579  				vecData[j] = v.Data[i*v.Cols+j]
   580  			}
   581  		}
   582  		vec := blas64.Vector{
   583  			Inc:  1,
   584  			Data: vecData,
   585  		}
   586  
   587  		hi := blas64.General{
   588  			Rows:   m,
   589  			Cols:   m,
   590  			Stride: m,
   591  			Data:   make([]float64, m*m),
   592  		}
   593  		for i := 0; i < m; i++ {
   594  			hi.Data[i*m+i] = 1
   595  		}
   596  		// hi = I - tau * v * vᵀ
   597  		blas64.Ger(-tau[i], vec, vec, hi)
   598  
   599  		hcopy := blas64.General{
   600  			Rows:   m,
   601  			Cols:   m,
   602  			Stride: m,
   603  			Data:   make([]float64, m*m),
   604  		}
   605  		copy(hcopy.Data, h.Data)
   606  		if direct == lapack.Forward {
   607  			// H = H * H_I in forward mode
   608  			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hcopy, hi, 0, h)
   609  		} else {
   610  			// H = H_I * H in backward mode
   611  			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hi, hcopy, 0, h)
   612  		}
   613  	}
   614  	return h
   615  }
   616  
   617  // constructQ constructs the Q matrix from the result of dgeqrf and dgeqr2.
   618  func constructQ(kind string, m, n int, a []float64, lda int, tau []float64) blas64.General {
   619  	k := min(m, n)
   620  	return constructQK(kind, m, n, k, a, lda, tau)
   621  }
   622  
   623  // constructQK constructs the Q matrix from the result of dgeqrf and dgeqr2 using
   624  // the first k reflectors.
   625  func constructQK(kind string, m, n, k int, a []float64, lda int, tau []float64) blas64.General {
   626  	var sz int
   627  	switch kind {
   628  	case "QR":
   629  		sz = m
   630  	case "LQ", "RQ":
   631  		sz = n
   632  	}
   633  
   634  	q := blas64.General{
   635  		Rows:   sz,
   636  		Cols:   sz,
   637  		Stride: max(1, sz),
   638  		Data:   make([]float64, sz*sz),
   639  	}
   640  	for i := 0; i < sz; i++ {
   641  		q.Data[i*sz+i] = 1
   642  	}
   643  	qCopy := blas64.General{
   644  		Rows:   q.Rows,
   645  		Cols:   q.Cols,
   646  		Stride: q.Stride,
   647  		Data:   make([]float64, len(q.Data)),
   648  	}
   649  	for i := 0; i < k; i++ {
   650  		h := blas64.General{
   651  			Rows:   sz,
   652  			Cols:   sz,
   653  			Stride: max(1, sz),
   654  			Data:   make([]float64, sz*sz),
   655  		}
   656  		for j := 0; j < sz; j++ {
   657  			h.Data[j*sz+j] = 1
   658  		}
   659  		vVec := blas64.Vector{
   660  			Inc:  1,
   661  			Data: make([]float64, sz),
   662  		}
   663  		switch kind {
   664  		case "QR":
   665  			vVec.Data[i] = 1
   666  			for j := i + 1; j < sz; j++ {
   667  				vVec.Data[j] = a[lda*j+i]
   668  			}
   669  		case "LQ":
   670  			vVec.Data[i] = 1
   671  			for j := i + 1; j < sz; j++ {
   672  				vVec.Data[j] = a[i*lda+j]
   673  			}
   674  		case "RQ":
   675  			for j := 0; j < n-k+i; j++ {
   676  				vVec.Data[j] = a[(m-k+i)*lda+j]
   677  			}
   678  			vVec.Data[n-k+i] = 1
   679  		}
   680  		blas64.Ger(-tau[i], vVec, vVec, h)
   681  		copy(qCopy.Data, q.Data)
   682  		// Multiply q by the new h.
   683  		switch kind {
   684  		case "QR", "RQ":
   685  			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, h, 0, q)
   686  		case "LQ":
   687  			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, h, qCopy, 0, q)
   688  		}
   689  	}
   690  	return q
   691  }
   692  
   693  // checkBidiagonal checks the bidiagonal decomposition from dlabrd and dgebd2.
   694  // The input to this function is the answer returned from the routines, stored
   695  // in a, d, e, tauP, and tauQ. The data of original A matrix (before
   696  // decomposition) is input in aCopy.
   697  //
   698  // checkBidiagonal constructs the V and U matrices, and from them constructs Q
   699  // and P. Using these constructions, it checks that Qᵀ * A * P and checks that
   700  // the result is bidiagonal.
   701  func checkBidiagonal(t *testing.T, m, n, nb int, a []float64, lda int, d, e, tauP, tauQ, aCopy []float64) {
   702  	// Check the answer.
   703  	// Construct V and U.
   704  	qMat := constructQPBidiagonal(lapack.ApplyQ, m, n, nb, a, lda, tauQ)
   705  	pMat := constructQPBidiagonal(lapack.ApplyP, m, n, nb, a, lda, tauP)
   706  
   707  	// Compute Qᵀ * A * P.
   708  	aMat := blas64.General{
   709  		Rows:   m,
   710  		Cols:   n,
   711  		Stride: lda,
   712  		Data:   make([]float64, len(aCopy)),
   713  	}
   714  	copy(aMat.Data, aCopy)
   715  
   716  	tmp1 := blas64.General{
   717  		Rows:   m,
   718  		Cols:   n,
   719  		Stride: n,
   720  		Data:   make([]float64, m*n),
   721  	}
   722  	blas64.Gemm(blas.Trans, blas.NoTrans, 1, qMat, aMat, 0, tmp1)
   723  	tmp2 := blas64.General{
   724  		Rows:   m,
   725  		Cols:   n,
   726  		Stride: n,
   727  		Data:   make([]float64, m*n),
   728  	}
   729  	blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, tmp1, pMat, 0, tmp2)
   730  
   731  	// Check that the first nb rows and cols of tm2 are upper bidiagonal
   732  	// if m >= n, and lower bidiagonal otherwise.
   733  	correctDiag := true
   734  	matchD := true
   735  	matchE := true
   736  	for i := 0; i < m; i++ {
   737  		for j := 0; j < n; j++ {
   738  			if i >= nb && j >= nb {
   739  				continue
   740  			}
   741  			v := tmp2.Data[i*tmp2.Stride+j]
   742  			if i == j {
   743  				if math.Abs(d[i]-v) > 1e-12 {
   744  					matchD = false
   745  				}
   746  				continue
   747  			}
   748  			if m >= n && i == j-1 {
   749  				if math.Abs(e[j-1]-v) > 1e-12 {
   750  					matchE = false
   751  				}
   752  				continue
   753  			}
   754  			if m < n && i-1 == j {
   755  				if math.Abs(e[i-1]-v) > 1e-12 {
   756  					matchE = false
   757  				}
   758  				continue
   759  			}
   760  			if math.Abs(v) > 1e-12 {
   761  				correctDiag = false
   762  			}
   763  		}
   764  	}
   765  	if !correctDiag {
   766  		t.Errorf("Updated A not bi-diagonal")
   767  	}
   768  	if !matchD {
   769  		fmt.Println("d = ", d)
   770  		t.Errorf("D Mismatch")
   771  	}
   772  	if !matchE {
   773  		t.Errorf("E mismatch")
   774  	}
   775  }
   776  
   777  // constructQPBidiagonal constructs Q or P from the Bidiagonal decomposition
   778  // computed by dlabrd and bgebd2.
   779  func constructQPBidiagonal(vect lapack.ApplyOrtho, m, n, nb int, a []float64, lda int, tau []float64) blas64.General {
   780  	sz := n
   781  	if vect == lapack.ApplyQ {
   782  		sz = m
   783  	}
   784  
   785  	var ldv int
   786  	var v blas64.General
   787  	if vect == lapack.ApplyQ {
   788  		ldv = nb
   789  		v = blas64.General{
   790  			Rows:   m,
   791  			Cols:   nb,
   792  			Stride: ldv,
   793  			Data:   make([]float64, m*ldv),
   794  		}
   795  	} else {
   796  		ldv = n
   797  		v = blas64.General{
   798  			Rows:   nb,
   799  			Cols:   n,
   800  			Stride: ldv,
   801  			Data:   make([]float64, m*ldv),
   802  		}
   803  	}
   804  
   805  	if vect == lapack.ApplyQ {
   806  		if m >= n {
   807  			for i := 0; i < m; i++ {
   808  				for j := 0; j <= min(nb-1, i); j++ {
   809  					if i == j {
   810  						v.Data[i*ldv+j] = 1
   811  						continue
   812  					}
   813  					v.Data[i*ldv+j] = a[i*lda+j]
   814  				}
   815  			}
   816  		} else {
   817  			for i := 1; i < m; i++ {
   818  				for j := 0; j <= min(nb-1, i-1); j++ {
   819  					if i-1 == j {
   820  						v.Data[i*ldv+j] = 1
   821  						continue
   822  					}
   823  					v.Data[i*ldv+j] = a[i*lda+j]
   824  				}
   825  			}
   826  		}
   827  	} else {
   828  		if m < n {
   829  			for i := 0; i < nb; i++ {
   830  				for j := i; j < n; j++ {
   831  					if i == j {
   832  						v.Data[i*ldv+j] = 1
   833  						continue
   834  					}
   835  					v.Data[i*ldv+j] = a[i*lda+j]
   836  				}
   837  			}
   838  		} else {
   839  			for i := 0; i < nb; i++ {
   840  				for j := i + 1; j < n; j++ {
   841  					if j-1 == i {
   842  						v.Data[i*ldv+j] = 1
   843  						continue
   844  					}
   845  					v.Data[i*ldv+j] = a[i*lda+j]
   846  				}
   847  			}
   848  		}
   849  	}
   850  
   851  	// The variable name is a computation of Q, but the algorithm is mostly the
   852  	// same for computing P (just with different data).
   853  	qMat := blas64.General{
   854  		Rows:   sz,
   855  		Cols:   sz,
   856  		Stride: sz,
   857  		Data:   make([]float64, sz*sz),
   858  	}
   859  	hMat := blas64.General{
   860  		Rows:   sz,
   861  		Cols:   sz,
   862  		Stride: sz,
   863  		Data:   make([]float64, sz*sz),
   864  	}
   865  	// set Q to I
   866  	for i := 0; i < sz; i++ {
   867  		qMat.Data[i*qMat.Stride+i] = 1
   868  	}
   869  	for i := 0; i < nb; i++ {
   870  		qCopy := blas64.General{Rows: qMat.Rows, Cols: qMat.Cols, Stride: qMat.Stride, Data: make([]float64, len(qMat.Data))}
   871  		copy(qCopy.Data, qMat.Data)
   872  
   873  		// Set g and h to I
   874  		for i := 0; i < sz; i++ {
   875  			for j := 0; j < sz; j++ {
   876  				if i == j {
   877  					hMat.Data[i*sz+j] = 1
   878  				} else {
   879  					hMat.Data[i*sz+j] = 0
   880  				}
   881  			}
   882  		}
   883  		var vi blas64.Vector
   884  		// H -= tauQ[i] * v[i] * v[i]^t
   885  		if vect == lapack.ApplyQ {
   886  			vi = blas64.Vector{
   887  				Inc:  v.Stride,
   888  				Data: v.Data[i:],
   889  			}
   890  		} else {
   891  			vi = blas64.Vector{
   892  				Inc:  1,
   893  				Data: v.Data[i*v.Stride:],
   894  			}
   895  		}
   896  		blas64.Ger(-tau[i], vi, vi, hMat)
   897  		// Q = Q * G[1]
   898  		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, hMat, 0, qMat)
   899  	}
   900  	return qMat
   901  }
   902  
   903  // printRowise prints the matrix with one row per line. This is useful for debugging.
   904  // If beyond is true, it prints beyond the final column to lda. If false, only
   905  // the columns are printed.
   906  //
   907  //lint:ignore U1000 This is useful for debugging.
   908  func printRowise(a []float64, m, n, lda int, beyond bool) {
   909  	for i := 0; i < m; i++ {
   910  		end := n
   911  		if beyond {
   912  			end = lda
   913  		}
   914  		fmt.Println(a[i*lda : i*lda+end])
   915  	}
   916  }
   917  
   918  func copyGeneral(dst, src blas64.General) {
   919  	r := min(dst.Rows, src.Rows)
   920  	c := min(dst.Cols, src.Cols)
   921  	for i := 0; i < r; i++ {
   922  		copy(dst.Data[i*dst.Stride:i*dst.Stride+c], src.Data[i*src.Stride:i*src.Stride+c])
   923  	}
   924  }
   925  
   926  // cloneGeneral allocates and returns an exact copy of the given general matrix.
   927  func cloneGeneral(a blas64.General) blas64.General {
   928  	c := a
   929  	c.Data = make([]float64, len(a.Data))
   930  	copy(c.Data, a.Data)
   931  	return c
   932  }
   933  
   934  // equalGeneral returns whether the general matrices a and b are equal.
   935  func equalGeneral(a, b blas64.General) bool {
   936  	if a.Rows != b.Rows || a.Cols != b.Cols {
   937  		panic("bad input")
   938  	}
   939  	for i := 0; i < a.Rows; i++ {
   940  		for j := 0; j < a.Cols; j++ {
   941  			if a.Data[i*a.Stride+j] != b.Data[i*b.Stride+j] {
   942  				return false
   943  			}
   944  		}
   945  	}
   946  	return true
   947  }
   948  
   949  // equalApproxGeneral returns whether the general matrices a and b are
   950  // approximately equal within given tolerance.
   951  func equalApproxGeneral(a, b blas64.General, tol float64) bool {
   952  	if a.Rows != b.Rows || a.Cols != b.Cols {
   953  		panic("bad input")
   954  	}
   955  	for i := 0; i < a.Rows; i++ {
   956  		for j := 0; j < a.Cols; j++ {
   957  			diff := a.Data[i*a.Stride+j] - b.Data[i*b.Stride+j]
   958  			if math.IsNaN(diff) || math.Abs(diff) > tol {
   959  				return false
   960  			}
   961  		}
   962  	}
   963  	return true
   964  }
   965  
   966  func intsEqual(a, b []int) bool {
   967  	if len(a) != len(b) {
   968  		return false
   969  	}
   970  	for i, ai := range a {
   971  		if b[i] != ai {
   972  			return false
   973  		}
   974  	}
   975  	return true
   976  }
   977  
   978  // randSymBand returns an n×n random symmetric positive definite band matrix
   979  // with kd diagonals.
   980  func randSymBand(uplo blas.Uplo, n, kd, ldab int, rnd *rand.Rand) []float64 {
   981  	// Allocate a triangular band matrix U or L and fill it with random numbers.
   982  	var ab []float64
   983  	if n > 0 {
   984  		ab = make([]float64, (n-1)*ldab+kd+1)
   985  	}
   986  	for i := range ab {
   987  		ab[i] = rnd.NormFloat64()
   988  	}
   989  	// Make sure that the matrix U or L has a sufficiently positive diagonal.
   990  	switch uplo {
   991  	case blas.Upper:
   992  		for i := 0; i < n; i++ {
   993  			ab[i*ldab] = float64(n) + rnd.Float64()
   994  		}
   995  	case blas.Lower:
   996  		for i := 0; i < n; i++ {
   997  			ab[i*ldab+kd] = float64(n) + rnd.Float64()
   998  		}
   999  	}
  1000  	// Compute Uᵀ*U or L*Lᵀ. The resulting (symmetric) matrix A will be
  1001  	// positive definite and well-conditioned.
  1002  	dsbmm(uplo, n, kd, ab, ldab)
  1003  	return ab
  1004  }
  1005  
  1006  // distSymBand returns the max-norm distance between the symmetric band matrices
  1007  // A and B.
  1008  func distSymBand(uplo blas.Uplo, n, kd int, a []float64, lda int, b []float64, ldb int) float64 {
  1009  	var dist float64
  1010  	switch uplo {
  1011  	case blas.Upper:
  1012  		for i := 0; i < n; i++ {
  1013  			for j := 0; j < min(kd+1, n-i); j++ {
  1014  				dist = math.Max(dist, math.Abs(a[i*lda+j]-b[i*ldb+j]))
  1015  			}
  1016  		}
  1017  	case blas.Lower:
  1018  		for i := 0; i < n; i++ {
  1019  			for j := max(0, kd-i); j < kd+1; j++ {
  1020  				dist = math.Max(dist, math.Abs(a[i*lda+j]-b[i*ldb+j]))
  1021  			}
  1022  		}
  1023  	}
  1024  	return dist
  1025  }
  1026  
  1027  // eye returns an identity matrix of given order and stride.
  1028  func eye(n, stride int) blas64.General {
  1029  	ans := nanGeneral(n, n, stride)
  1030  	for i := 0; i < n; i++ {
  1031  		for j := 0; j < n; j++ {
  1032  			ans.Data[i*ans.Stride+j] = 0
  1033  		}
  1034  		ans.Data[i*ans.Stride+i] = 1
  1035  	}
  1036  	return ans
  1037  }
  1038  
  1039  // zeros returns an m×n matrix with given stride filled with zeros.
  1040  func zeros(m, n, stride int) blas64.General {
  1041  	a := nanGeneral(m, n, stride)
  1042  	for i := 0; i < m; i++ {
  1043  		for j := 0; j < n; j++ {
  1044  			a.Data[i*a.Stride+j] = 0
  1045  		}
  1046  	}
  1047  	return a
  1048  }
  1049  
  1050  // extract2x2Block returns the elements of T at [0,0], [0,1], [1,0], and [1,1].
  1051  func extract2x2Block(t []float64, ldt int) (a, b, c, d float64) {
  1052  	return t[0], t[1], t[ldt], t[ldt+1]
  1053  }
  1054  
  1055  // isSchurCanonical returns whether the 2×2 matrix [a b; c d] is in Schur
  1056  // canonical form.
  1057  func isSchurCanonical(a, b, c, d float64) bool {
  1058  	return c == 0 || (b != 0 && a == d && math.Signbit(b) != math.Signbit(c))
  1059  }
  1060  
  1061  // isSchurCanonicalGeneral returns whether T is block upper triangular with 1×1
  1062  // and 2×2 diagonal blocks, each 2×2 block in Schur canonical form. The function
  1063  // checks only along the diagonal and the first subdiagonal, otherwise the lower
  1064  // triangle is not accessed.
  1065  func isSchurCanonicalGeneral(t blas64.General) bool {
  1066  	n := t.Cols
  1067  	if t.Rows != n {
  1068  		panic("invalid matrix")
  1069  	}
  1070  	for j := 0; j < n-1; {
  1071  		if t.Data[(j+1)*t.Stride+j] == 0 {
  1072  			// 1×1 block.
  1073  			for i := j + 1; i < n; i++ {
  1074  				if t.Data[i*t.Stride+j] != 0 {
  1075  					return false
  1076  				}
  1077  			}
  1078  			j++
  1079  			continue
  1080  		}
  1081  		// 2×2 block.
  1082  		a, b, c, d := extract2x2Block(t.Data[j*t.Stride+j:], t.Stride)
  1083  		if !isSchurCanonical(a, b, c, d) {
  1084  			return false
  1085  		}
  1086  		for i := j + 2; i < n; i++ {
  1087  			if t.Data[i*t.Stride+j] != 0 {
  1088  				return false
  1089  			}
  1090  		}
  1091  		for i := j + 2; i < n; i++ {
  1092  			if t.Data[i*t.Stride+j+1] != 0 {
  1093  				return false
  1094  			}
  1095  		}
  1096  		j += 2
  1097  	}
  1098  	return true
  1099  }
  1100  
  1101  // schurBlockEigenvalues returns the two eigenvalues of the 2×2 matrix [a b; c d]
  1102  // that must be in Schur canonical form.
  1103  //
  1104  //lint:ignore U1000 This is useful for debugging.
  1105  func schurBlockEigenvalues(a, b, c, d float64) (ev1, ev2 complex128) {
  1106  	if !isSchurCanonical(a, b, c, d) {
  1107  		panic("block not in Schur canonical form")
  1108  	}
  1109  	if c == 0 {
  1110  		return complex(a, 0), complex(d, 0)
  1111  	}
  1112  	im := math.Sqrt(math.Abs(b)) * math.Sqrt(math.Abs(c))
  1113  	return complex(a, im), complex(a, -im)
  1114  }
  1115  
  1116  // schurBlockSize returns the size of the diagonal block at i-th row in the
  1117  // upper quasi-triangular matrix t in Schur canonical form, and whether i points
  1118  // to the first row of the block. For zero-sized matrices the function returns 0
  1119  // and true.
  1120  func schurBlockSize(t blas64.General, i int) (size int, first bool) {
  1121  	if t.Rows != t.Cols {
  1122  		panic("matrix not square")
  1123  	}
  1124  	if t.Rows == 0 {
  1125  		return 0, true
  1126  	}
  1127  	if i < 0 || t.Rows <= i {
  1128  		panic("index out of range")
  1129  	}
  1130  
  1131  	first = true
  1132  	if i > 0 && t.Data[i*t.Stride+i-1] != 0 {
  1133  		// There is a non-zero element to the left, therefore i must
  1134  		// point to the second row in a 2×2 diagonal block.
  1135  		first = false
  1136  		i--
  1137  	}
  1138  	size = 1
  1139  	if i+1 < t.Rows && t.Data[(i+1)*t.Stride+i] != 0 {
  1140  		// There is a non-zero element below, this must be a 2×2
  1141  		// diagonal block.
  1142  		size = 2
  1143  	}
  1144  	return size, first
  1145  }
  1146  
  1147  // containsComplex returns whether z is approximately equal to one of the complex
  1148  // numbers in v. If z is found, its index in v will be also returned.
  1149  func containsComplex(v []complex128, z complex128, tol float64) (found bool, index int) {
  1150  	for i := range v {
  1151  		if cmplx.Abs(v[i]-z) < tol {
  1152  			return true, i
  1153  		}
  1154  	}
  1155  	return false, -1
  1156  }
  1157  
  1158  // isAllNaN returns whether x contains only NaN values.
  1159  func isAllNaN(x []float64) bool {
  1160  	for _, v := range x {
  1161  		if !math.IsNaN(v) {
  1162  			return false
  1163  		}
  1164  	}
  1165  	return true
  1166  }
  1167  
  1168  // isUpperHessenberg returns whether h contains only zeros below the
  1169  // subdiagonal.
  1170  func isUpperHessenberg(h blas64.General) bool {
  1171  	if h.Rows != h.Cols {
  1172  		panic("matrix not square")
  1173  	}
  1174  	n := h.Rows
  1175  	for i := 0; i < n; i++ {
  1176  		for j := 0; j < n; j++ {
  1177  			if i > j+1 && h.Data[i*h.Stride+j] != 0 {
  1178  				return false
  1179  			}
  1180  		}
  1181  	}
  1182  	return true
  1183  }
  1184  
  1185  // isUpperTriangular returns whether a contains only zeros below the diagonal.
  1186  func isUpperTriangular(a blas64.General) bool {
  1187  	if a.Rows != a.Cols {
  1188  		panic("matrix not square")
  1189  	}
  1190  	n := a.Rows
  1191  	for i := 1; i < n; i++ {
  1192  		for j := 0; j < i; j++ {
  1193  			if a.Data[i*a.Stride+j] != 0 {
  1194  				return false
  1195  			}
  1196  		}
  1197  	}
  1198  	return true
  1199  }
  1200  
  1201  // unbalancedSparseGeneral returns an m×n dense matrix with a random sparse
  1202  // structure consisting of nz nonzero elements. The matrix will be unbalanced by
  1203  // multiplying each element randomly by its row or column index.
  1204  func unbalancedSparseGeneral(m, n, stride int, nonzeros int, rnd *rand.Rand) blas64.General {
  1205  	a := zeros(m, n, stride)
  1206  	for k := 0; k < nonzeros; k++ {
  1207  		i := rnd.Intn(n)
  1208  		j := rnd.Intn(n)
  1209  		if rnd.Float64() < 0.5 {
  1210  			a.Data[i*stride+j] = float64(i+1) * rnd.NormFloat64()
  1211  		} else {
  1212  			a.Data[i*stride+j] = float64(j+1) * rnd.NormFloat64()
  1213  		}
  1214  	}
  1215  	return a
  1216  }
  1217  
  1218  // rootsOfUnity returns the n complex numbers whose n-th power is equal to 1.
  1219  func rootsOfUnity(n int) []complex128 {
  1220  	w := make([]complex128, n)
  1221  	for i := 0; i < n; i++ {
  1222  		angle := math.Pi * float64(2*i) / float64(n)
  1223  		w[i] = complex(math.Cos(angle), math.Sin(angle))
  1224  	}
  1225  	return w
  1226  }
  1227  
  1228  // constructGSVDresults returns the matrices [ 0 R ], D1 and D2 described
  1229  // in the documentation of Dtgsja and Dggsvd3, and the result matrix in
  1230  // the documentation for Dggsvp3.
  1231  func constructGSVDresults(n, p, m, k, l int, a, b blas64.General, alpha, beta []float64) (zeroR, d1, d2 blas64.General) {
  1232  	// [ 0 R ]
  1233  	zeroR = zeros(k+l, n, n)
  1234  	dst := zeroR
  1235  	dst.Rows = min(m, k+l)
  1236  	dst.Cols = k + l
  1237  	dst.Data = zeroR.Data[n-k-l:]
  1238  	src := a
  1239  	src.Rows = min(m, k+l)
  1240  	src.Cols = k + l
  1241  	src.Data = a.Data[n-k-l:]
  1242  	copyGeneral(dst, src)
  1243  	if m < k+l {
  1244  		// [ 0 R ]
  1245  		dst.Rows = k + l - m
  1246  		dst.Cols = k + l - m
  1247  		dst.Data = zeroR.Data[m*zeroR.Stride+n-(k+l-m):]
  1248  		src = b
  1249  		src.Rows = k + l - m
  1250  		src.Cols = k + l - m
  1251  		src.Data = b.Data[(m-k)*b.Stride+n+m-k-l:]
  1252  		copyGeneral(dst, src)
  1253  	}
  1254  
  1255  	// D1
  1256  	d1 = zeros(m, k+l, k+l)
  1257  	for i := 0; i < k; i++ {
  1258  		d1.Data[i*d1.Stride+i] = 1
  1259  	}
  1260  	for i := k; i < min(m, k+l); i++ {
  1261  		d1.Data[i*d1.Stride+i] = alpha[i]
  1262  	}
  1263  
  1264  	// D2
  1265  	d2 = zeros(p, k+l, k+l)
  1266  	for i := 0; i < min(l, m-k); i++ {
  1267  		d2.Data[i*d2.Stride+i+k] = beta[k+i]
  1268  	}
  1269  	for i := m - k; i < l; i++ {
  1270  		d2.Data[i*d2.Stride+i+k] = 1
  1271  	}
  1272  
  1273  	return zeroR, d1, d2
  1274  }
  1275  
  1276  func constructGSVPresults(n, p, m, k, l int, a, b blas64.General) (zeroA, zeroB blas64.General) {
  1277  	zeroA = zeros(m, n, n)
  1278  	dst := zeroA
  1279  	dst.Rows = min(m, k+l)
  1280  	dst.Cols = k + l
  1281  	dst.Data = zeroA.Data[n-k-l:]
  1282  	src := a
  1283  	dst.Rows = min(m, k+l)
  1284  	src.Cols = k + l
  1285  	src.Data = a.Data[n-k-l:]
  1286  	copyGeneral(dst, src)
  1287  
  1288  	zeroB = zeros(p, n, n)
  1289  	dst = zeroB
  1290  	dst.Rows = l
  1291  	dst.Cols = l
  1292  	dst.Data = zeroB.Data[n-l:]
  1293  	src = b
  1294  	dst.Rows = l
  1295  	src.Cols = l
  1296  	src.Data = b.Data[n-l:]
  1297  	copyGeneral(dst, src)
  1298  
  1299  	return zeroA, zeroB
  1300  }
  1301  
  1302  // distFromIdentity returns the L-infinity distance of an n×n matrix A from the
  1303  // identity. If A contains NaN elements, distFromIdentity will return +inf.
  1304  func distFromIdentity(n int, a []float64, lda int) float64 {
  1305  	var dist float64
  1306  	for i := 0; i < n; i++ {
  1307  		for j := 0; j < n; j++ {
  1308  			aij := a[i*lda+j]
  1309  			if math.IsNaN(aij) {
  1310  				return math.Inf(1)
  1311  			}
  1312  			if i == j {
  1313  				dist = math.Max(dist, math.Abs(aij-1))
  1314  			} else {
  1315  				dist = math.Max(dist, math.Abs(aij))
  1316  			}
  1317  		}
  1318  	}
  1319  	return dist
  1320  }
  1321  
  1322  func sameFloat64(a, b float64) bool {
  1323  	return a == b || math.IsNaN(a) && math.IsNaN(b)
  1324  }
  1325  
  1326  // sameLowerTri returns whether n×n matrices A and B are same under the diagonal.
  1327  func sameLowerTri(n int, a []float64, lda int, b []float64, ldb int) bool {
  1328  	for i := 1; i < n; i++ {
  1329  		for j := 0; j < i; j++ {
  1330  			aij := a[i*lda+j]
  1331  			bij := b[i*ldb+j]
  1332  			if !sameFloat64(aij, bij) {
  1333  				return false
  1334  			}
  1335  		}
  1336  	}
  1337  	return true
  1338  }
  1339  
  1340  // sameUpperTri returns whether n×n matrices A and B are same above the diagonal.
  1341  func sameUpperTri(n int, a []float64, lda int, b []float64, ldb int) bool {
  1342  	for i := 0; i < n-1; i++ {
  1343  		for j := i + 1; j < n; j++ {
  1344  			aij := a[i*lda+j]
  1345  			bij := b[i*ldb+j]
  1346  			if !sameFloat64(aij, bij) {
  1347  				return false
  1348  			}
  1349  		}
  1350  	}
  1351  	return true
  1352  }
  1353  
  1354  // svdJobString returns a string representation of job.
  1355  func svdJobString(job lapack.SVDJob) string {
  1356  	switch job {
  1357  	case lapack.SVDAll:
  1358  		return "All"
  1359  	case lapack.SVDStore:
  1360  		return "Store"
  1361  	case lapack.SVDOverwrite:
  1362  		return "Overwrite"
  1363  	case lapack.SVDNone:
  1364  		return "None"
  1365  	}
  1366  	return "unknown SVD job"
  1367  }
  1368  
  1369  // residualOrthogonal returns the residual
  1370  //
  1371  //	|I - Q * Qᵀ|  if m < n or (m == n and rowwise == true),
  1372  //	|I - Qᵀ * Q|  otherwise.
  1373  //
  1374  // It can be used to check that the matrix Q is orthogonal.
  1375  func residualOrthogonal(q blas64.General, rowwise bool) float64 {
  1376  	m, n := q.Rows, q.Cols
  1377  	if m == 0 || n == 0 {
  1378  		return 0
  1379  	}
  1380  	var transq blas.Transpose
  1381  	if m < n || (m == n && rowwise) {
  1382  		transq = blas.NoTrans
  1383  	} else {
  1384  		transq = blas.Trans
  1385  	}
  1386  	minmn := min(m, n)
  1387  
  1388  	// Set work = I.
  1389  	work := blas64.Symmetric{
  1390  		Uplo:   blas.Upper,
  1391  		N:      minmn,
  1392  		Data:   make([]float64, minmn*minmn),
  1393  		Stride: minmn,
  1394  	}
  1395  	for i := 0; i < minmn; i++ {
  1396  		work.Data[i*work.Stride+i] = 1
  1397  	}
  1398  
  1399  	// Compute
  1400  	//  work = work - Q * Qᵀ = I - Q * Qᵀ
  1401  	// or
  1402  	//  work = work - Qᵀ * Q = I - Qᵀ * Q
  1403  	blas64.Syrk(transq, -1, q, 1, work)
  1404  	return dlansy(lapack.MaxColumnSum, blas.Upper, work.N, work.Data, work.Stride)
  1405  }