gonum.org/v1/gonum@v0.14.0/lapack/gonum/dgels.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 gonum
     6  
     7  import (
     8  	"gonum.org/v1/gonum/blas"
     9  	"gonum.org/v1/gonum/lapack"
    10  )
    11  
    12  // Dgels finds a minimum-norm solution based on the matrices A and B using the
    13  // QR or LQ factorization. Dgels returns false if the matrix
    14  // A is singular, and true if this solution was successfully found.
    15  //
    16  // The minimization problem solved depends on the input parameters.
    17  //
    18  //  1. If m >= n and trans == blas.NoTrans, Dgels finds X such that || A*X - B||_2
    19  //     is minimized.
    20  //  2. If m < n and trans == blas.NoTrans, Dgels finds the minimum norm solution of
    21  //     A * X = B.
    22  //  3. If m >= n and trans == blas.Trans, Dgels finds the minimum norm solution of
    23  //     Aᵀ * X = B.
    24  //  4. If m < n and trans == blas.Trans, Dgels finds X such that || A*X - B||_2
    25  //     is minimized.
    26  //
    27  // Note that the least-squares solutions (cases 1 and 3) perform the minimization
    28  // per column of B. This is not the same as finding the minimum-norm matrix.
    29  //
    30  // The matrix A is a general matrix of size m×n and is modified during this call.
    31  // The input matrix B is of size max(m,n)×nrhs, and serves two purposes. On entry,
    32  // the elements of b specify the input matrix B. B has size m×nrhs if
    33  // trans == blas.NoTrans, and n×nrhs if trans == blas.Trans. On exit, the
    34  // leading submatrix of b contains the solution vectors X. If trans == blas.NoTrans,
    35  // this submatrix is of size n×nrhs, and of size m×nrhs otherwise.
    36  //
    37  // work is temporary storage, and lwork specifies the usable memory length.
    38  // At minimum, lwork >= max(m,n) + max(m,n,nrhs), and this function will panic
    39  // otherwise. A longer work will enable blocked algorithms to be called.
    40  // In the special case that lwork == -1, work[0] will be set to the optimal working
    41  // length.
    42  func (impl Implementation) Dgels(trans blas.Transpose, m, n, nrhs int, a []float64, lda int, b []float64, ldb int, work []float64, lwork int) bool {
    43  	mn := min(m, n)
    44  	minwrk := mn + max(mn, nrhs)
    45  	switch {
    46  	case trans != blas.NoTrans && trans != blas.Trans && trans != blas.ConjTrans:
    47  		panic(badTrans)
    48  	case m < 0:
    49  		panic(mLT0)
    50  	case n < 0:
    51  		panic(nLT0)
    52  	case nrhs < 0:
    53  		panic(nrhsLT0)
    54  	case lda < max(1, n):
    55  		panic(badLdA)
    56  	case ldb < max(1, nrhs):
    57  		panic(badLdB)
    58  	case lwork < max(1, minwrk) && lwork != -1:
    59  		panic(badLWork)
    60  	case len(work) < max(1, lwork):
    61  		panic(shortWork)
    62  	}
    63  
    64  	// Quick return if possible.
    65  	if mn == 0 || nrhs == 0 {
    66  		impl.Dlaset(blas.All, max(m, n), nrhs, 0, 0, b, ldb)
    67  		work[0] = 1
    68  		return true
    69  	}
    70  
    71  	// Find optimal block size.
    72  	var nb int
    73  	if m >= n {
    74  		nb = impl.Ilaenv(1, "DGEQRF", " ", m, n, -1, -1)
    75  		if trans != blas.NoTrans {
    76  			nb = max(nb, impl.Ilaenv(1, "DORMQR", "LN", m, nrhs, n, -1))
    77  		} else {
    78  			nb = max(nb, impl.Ilaenv(1, "DORMQR", "LT", m, nrhs, n, -1))
    79  		}
    80  	} else {
    81  		nb = impl.Ilaenv(1, "DGELQF", " ", m, n, -1, -1)
    82  		if trans != blas.NoTrans {
    83  			nb = max(nb, impl.Ilaenv(1, "DORMLQ", "LT", n, nrhs, m, -1))
    84  		} else {
    85  			nb = max(nb, impl.Ilaenv(1, "DORMLQ", "LN", n, nrhs, m, -1))
    86  		}
    87  	}
    88  	wsize := max(1, mn+max(mn, nrhs)*nb)
    89  	work[0] = float64(wsize)
    90  
    91  	if lwork == -1 {
    92  		return true
    93  	}
    94  
    95  	switch {
    96  	case len(a) < (m-1)*lda+n:
    97  		panic(shortA)
    98  	case len(b) < (max(m, n)-1)*ldb+nrhs:
    99  		panic(shortB)
   100  	}
   101  
   102  	// Scale the input matrices if they contain extreme values.
   103  	smlnum := dlamchS / dlamchP
   104  	bignum := 1 / smlnum
   105  	anrm := impl.Dlange(lapack.MaxAbs, m, n, a, lda, nil)
   106  	var iascl int
   107  	if anrm > 0 && anrm < smlnum {
   108  		impl.Dlascl(lapack.General, 0, 0, anrm, smlnum, m, n, a, lda)
   109  		iascl = 1
   110  	} else if anrm > bignum {
   111  		impl.Dlascl(lapack.General, 0, 0, anrm, bignum, m, n, a, lda)
   112  	} else if anrm == 0 {
   113  		// Matrix is all zeros.
   114  		impl.Dlaset(blas.All, max(m, n), nrhs, 0, 0, b, ldb)
   115  		return true
   116  	}
   117  	brow := m
   118  	if trans != blas.NoTrans {
   119  		brow = n
   120  	}
   121  	bnrm := impl.Dlange(lapack.MaxAbs, brow, nrhs, b, ldb, nil)
   122  	ibscl := 0
   123  	if bnrm > 0 && bnrm < smlnum {
   124  		impl.Dlascl(lapack.General, 0, 0, bnrm, smlnum, brow, nrhs, b, ldb)
   125  		ibscl = 1
   126  	} else if bnrm > bignum {
   127  		impl.Dlascl(lapack.General, 0, 0, bnrm, bignum, brow, nrhs, b, ldb)
   128  		ibscl = 2
   129  	}
   130  
   131  	// Solve the minimization problem using a QR or an LQ decomposition.
   132  	var scllen int
   133  	if m >= n {
   134  		impl.Dgeqrf(m, n, a, lda, work, work[mn:], lwork-mn)
   135  		if trans == blas.NoTrans {
   136  			impl.Dormqr(blas.Left, blas.Trans, m, nrhs, n,
   137  				a, lda,
   138  				work[:n],
   139  				b, ldb,
   140  				work[mn:], lwork-mn)
   141  			ok := impl.Dtrtrs(blas.Upper, blas.NoTrans, blas.NonUnit, n, nrhs,
   142  				a, lda,
   143  				b, ldb)
   144  			if !ok {
   145  				return false
   146  			}
   147  			scllen = n
   148  		} else {
   149  			ok := impl.Dtrtrs(blas.Upper, blas.Trans, blas.NonUnit, n, nrhs,
   150  				a, lda,
   151  				b, ldb)
   152  			if !ok {
   153  				return false
   154  			}
   155  			for i := n; i < m; i++ {
   156  				for j := 0; j < nrhs; j++ {
   157  					b[i*ldb+j] = 0
   158  				}
   159  			}
   160  			impl.Dormqr(blas.Left, blas.NoTrans, m, nrhs, n,
   161  				a, lda,
   162  				work[:n],
   163  				b, ldb,
   164  				work[mn:], lwork-mn)
   165  			scllen = m
   166  		}
   167  	} else {
   168  		impl.Dgelqf(m, n, a, lda, work, work[mn:], lwork-mn)
   169  		if trans == blas.NoTrans {
   170  			ok := impl.Dtrtrs(blas.Lower, blas.NoTrans, blas.NonUnit,
   171  				m, nrhs,
   172  				a, lda,
   173  				b, ldb)
   174  			if !ok {
   175  				return false
   176  			}
   177  			for i := m; i < n; i++ {
   178  				for j := 0; j < nrhs; j++ {
   179  					b[i*ldb+j] = 0
   180  				}
   181  			}
   182  			impl.Dormlq(blas.Left, blas.Trans, n, nrhs, m,
   183  				a, lda,
   184  				work,
   185  				b, ldb,
   186  				work[mn:], lwork-mn)
   187  			scllen = n
   188  		} else {
   189  			impl.Dormlq(blas.Left, blas.NoTrans, n, nrhs, m,
   190  				a, lda,
   191  				work,
   192  				b, ldb,
   193  				work[mn:], lwork-mn)
   194  			ok := impl.Dtrtrs(blas.Lower, blas.Trans, blas.NonUnit,
   195  				m, nrhs,
   196  				a, lda,
   197  				b, ldb)
   198  			if !ok {
   199  				return false
   200  			}
   201  		}
   202  	}
   203  
   204  	// Adjust answer vector based on scaling.
   205  	if iascl == 1 {
   206  		impl.Dlascl(lapack.General, 0, 0, anrm, smlnum, scllen, nrhs, b, ldb)
   207  	}
   208  	if iascl == 2 {
   209  		impl.Dlascl(lapack.General, 0, 0, anrm, bignum, scllen, nrhs, b, ldb)
   210  	}
   211  	if ibscl == 1 {
   212  		impl.Dlascl(lapack.General, 0, 0, smlnum, bnrm, scllen, nrhs, b, ldb)
   213  	}
   214  	if ibscl == 2 {
   215  		impl.Dlascl(lapack.General, 0, 0, bignum, bnrm, scllen, nrhs, b, ldb)
   216  	}
   217  
   218  	work[0] = float64(wsize)
   219  	return true
   220  }