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