github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/lapack/gonum/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 gonum
     6  
     7  import "math"
     8  
     9  // Dlaln2 solves a linear equation or a system of 2 linear equations of the form
    10  //  (ca A   - w D) X = scale B  if trans == false,
    11  //  (ca Aᵀ - w D) X = scale B   if trans == true,
    12  // where A is a na×na real matrix, ca is a real scalar, D is a na×na diagonal
    13  // real matrix, w is a scalar, real if nw == 1, complex if nw == 2, and X and B
    14  // are na×1 matrices, real if w is real, complex if w is complex.
    15  //
    16  // If w is complex, X and B are represented as na×2 matrices, the first column
    17  // of each being the real part and the second being the imaginary part.
    18  //
    19  // na and nw must be 1 or 2, otherwise Dlaln2 will panic.
    20  //
    21  // d1 and d2 are the diagonal elements of D. d2 is not used if na == 1.
    22  //
    23  // wr and wi represent the real and imaginary part, respectively, of the scalar
    24  // w. wi is not used if nw == 1.
    25  //
    26  // smin is the desired lower bound on the singular values of A. This should be
    27  // a safe distance away from underflow or overflow, say, between
    28  // (underflow/machine precision) and (overflow*machine precision).
    29  //
    30  // If both singular values of (ca A - w D) are less than smin, smin*identity
    31  // will be used instead of (ca A - w D). If only one singular value is less than
    32  // smin, one element of (ca A - w D) will be perturbed enough to make the
    33  // smallest singular value roughly smin. If both singular values are at least
    34  // smin, (ca A - w D) will not be perturbed. In any case, the perturbation will
    35  // be at most some small multiple of max(smin, ulp*norm(ca A - w D)). The
    36  // singular values are computed by infinity-norm approximations, and thus will
    37  // only be correct to a factor of 2 or so.
    38  //
    39  // All input quantities are assumed to be smaller than overflow by a reasonable
    40  // factor.
    41  //
    42  // scale is a scaling factor less than or equal to 1 which is chosen so that X
    43  // can be computed without overflow. X is further scaled if necessary to assure
    44  // that norm(ca A - w D)*norm(X) is less than overflow.
    45  //
    46  // xnorm contains the infinity-norm of X when X is regarded as a na×nw real
    47  // matrix.
    48  //
    49  // ok will be false if (ca A - w D) had to be perturbed to make its smallest
    50  // singular value greater than smin, otherwise ok will be true.
    51  //
    52  // Dlaln2 is an internal routine. It is exported for testing purposes.
    53  func (impl Implementation) 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) {
    54  	// TODO(vladimir-ch): Consider splitting this function into two, one
    55  	// handling the real case (nw == 1) and the other handling the complex
    56  	// case (nw == 2). Given that Go has complex types, their signatures
    57  	// would be simpler and more natural, and the implementation not as
    58  	// convoluted.
    59  
    60  	switch {
    61  	case na != 1 && na != 2:
    62  		panic(badNa)
    63  	case nw != 1 && nw != 2:
    64  		panic(badNw)
    65  	case lda < na:
    66  		panic(badLdA)
    67  	case len(a) < (na-1)*lda+na:
    68  		panic(shortA)
    69  	case ldb < nw:
    70  		panic(badLdB)
    71  	case len(b) < (na-1)*ldb+nw:
    72  		panic(shortB)
    73  	case ldx < nw:
    74  		panic(badLdX)
    75  	case len(x) < (na-1)*ldx+nw:
    76  		panic(shortX)
    77  	}
    78  
    79  	smlnum := 2 * dlamchS
    80  	bignum := 1 / smlnum
    81  	smini := math.Max(smin, smlnum)
    82  
    83  	ok = true
    84  	scale = 1
    85  
    86  	if na == 1 {
    87  		// 1×1 (i.e., scalar) system C X = B.
    88  
    89  		if nw == 1 {
    90  			// Real 1×1 system.
    91  
    92  			// C = ca A - w D.
    93  			csr := ca*a[0] - wr*d1
    94  			cnorm := math.Abs(csr)
    95  
    96  			// If |C| < smini, use C = smini.
    97  			if cnorm < smini {
    98  				csr = smini
    99  				cnorm = smini
   100  				ok = false
   101  			}
   102  
   103  			// Check scaling for X = B / C.
   104  			bnorm := math.Abs(b[0])
   105  			if cnorm < 1 && bnorm > math.Max(1, bignum*cnorm) {
   106  				scale = 1 / bnorm
   107  			}
   108  
   109  			// Compute X.
   110  			x[0] = b[0] * scale / csr
   111  			xnorm = math.Abs(x[0])
   112  
   113  			return scale, xnorm, ok
   114  		}
   115  
   116  		// Complex 1×1 system (w is complex).
   117  
   118  		// C = ca A - w D.
   119  		csr := ca*a[0] - wr*d1
   120  		csi := -wi * d1
   121  		cnorm := math.Abs(csr) + math.Abs(csi)
   122  
   123  		// If |C| < smini, use C = smini.
   124  		if cnorm < smini {
   125  			csr = smini
   126  			csi = 0
   127  			cnorm = smini
   128  			ok = false
   129  		}
   130  
   131  		// Check scaling for X = B / C.
   132  		bnorm := math.Abs(b[0]) + math.Abs(b[1])
   133  		if cnorm < 1 && bnorm > math.Max(1, bignum*cnorm) {
   134  			scale = 1 / bnorm
   135  		}
   136  
   137  		// Compute X.
   138  		cx := complex(scale*b[0], scale*b[1]) / complex(csr, csi)
   139  		x[0], x[1] = real(cx), imag(cx)
   140  		xnorm = math.Abs(x[0]) + math.Abs(x[1])
   141  
   142  		return scale, xnorm, ok
   143  	}
   144  
   145  	// 2×2 system.
   146  
   147  	// Compute the real part of
   148  	//  C = ca A   - w D
   149  	// or
   150  	//  C = ca Aᵀ - w D.
   151  	crv := [4]float64{
   152  		ca*a[0] - wr*d1,
   153  		ca * a[1],
   154  		ca * a[lda],
   155  		ca*a[lda+1] - wr*d2,
   156  	}
   157  	if trans {
   158  		crv[1] = ca * a[lda]
   159  		crv[2] = ca * a[1]
   160  	}
   161  
   162  	pivot := [4][4]int{
   163  		{0, 1, 2, 3},
   164  		{1, 0, 3, 2},
   165  		{2, 3, 0, 1},
   166  		{3, 2, 1, 0},
   167  	}
   168  
   169  	if nw == 1 {
   170  		// Real 2×2 system (w is real).
   171  
   172  		// Find the largest element in C.
   173  		var cmax float64
   174  		var icmax int
   175  		for j, v := range crv {
   176  			v = math.Abs(v)
   177  			if v > cmax {
   178  				cmax = v
   179  				icmax = j
   180  			}
   181  		}
   182  
   183  		// If norm(C) < smini, use smini*identity.
   184  		if cmax < smini {
   185  			bnorm := math.Max(math.Abs(b[0]), math.Abs(b[ldb]))
   186  			if smini < 1 && bnorm > math.Max(1, bignum*smini) {
   187  				scale = 1 / bnorm
   188  			}
   189  			temp := scale / smini
   190  			x[0] = temp * b[0]
   191  			x[ldx] = temp * b[ldb]
   192  			xnorm = temp * bnorm
   193  			ok = false
   194  
   195  			return scale, xnorm, ok
   196  		}
   197  
   198  		// Gaussian elimination with complete pivoting.
   199  		// Form upper triangular matrix
   200  		//  [ur11 ur12]
   201  		//  [   0 ur22]
   202  		ur11 := crv[icmax]
   203  		ur12 := crv[pivot[icmax][1]]
   204  		cr21 := crv[pivot[icmax][2]]
   205  		cr22 := crv[pivot[icmax][3]]
   206  		ur11r := 1 / ur11
   207  		lr21 := ur11r * cr21
   208  		ur22 := cr22 - ur12*lr21
   209  
   210  		// If smaller pivot < smini, use smini.
   211  		if math.Abs(ur22) < smini {
   212  			ur22 = smini
   213  			ok = false
   214  		}
   215  
   216  		var br1, br2 float64
   217  		if icmax > 1 {
   218  			// If the pivot lies in the second row, swap the rows.
   219  			br1 = b[ldb]
   220  			br2 = b[0]
   221  		} else {
   222  			br1 = b[0]
   223  			br2 = b[ldb]
   224  		}
   225  		br2 -= lr21 * br1 // Apply the Gaussian elimination step to the right-hand side.
   226  
   227  		bbnd := math.Max(math.Abs(ur22*ur11r*br1), math.Abs(br2))
   228  		if bbnd > 1 && math.Abs(ur22) < 1 && bbnd >= bignum*math.Abs(ur22) {
   229  			scale = 1 / bbnd
   230  		}
   231  
   232  		// Solve the linear system ur*xr=br.
   233  		xr2 := br2 * scale / ur22
   234  		xr1 := scale*br1*ur11r - ur11r*ur12*xr2
   235  		if icmax&0x1 != 0 {
   236  			// If the pivot lies in the second column, swap the components of the solution.
   237  			x[0] = xr2
   238  			x[ldx] = xr1
   239  		} else {
   240  			x[0] = xr1
   241  			x[ldx] = xr2
   242  		}
   243  		xnorm = math.Max(math.Abs(xr1), math.Abs(xr2))
   244  
   245  		// Further scaling if norm(A)*norm(X) > overflow.
   246  		if xnorm > 1 && cmax > 1 && xnorm > bignum/cmax {
   247  			temp := cmax / bignum
   248  			x[0] *= temp
   249  			x[ldx] *= temp
   250  			xnorm *= temp
   251  			scale *= temp
   252  		}
   253  
   254  		return scale, xnorm, ok
   255  	}
   256  
   257  	// Complex 2×2 system (w is complex).
   258  
   259  	// Find the largest element in C.
   260  	civ := [4]float64{
   261  		-wi * d1,
   262  		0,
   263  		0,
   264  		-wi * d2,
   265  	}
   266  	var cmax float64
   267  	var icmax int
   268  	for j, v := range crv {
   269  		v := math.Abs(v)
   270  		if v+math.Abs(civ[j]) > cmax {
   271  			cmax = v + math.Abs(civ[j])
   272  			icmax = j
   273  		}
   274  	}
   275  
   276  	// If norm(C) < smini, use smini*identity.
   277  	if cmax < smini {
   278  		br1 := math.Abs(b[0]) + math.Abs(b[1])
   279  		br2 := math.Abs(b[ldb]) + math.Abs(b[ldb+1])
   280  		bnorm := math.Max(br1, br2)
   281  		if smini < 1 && bnorm > 1 && bnorm > bignum*smini {
   282  			scale = 1 / bnorm
   283  		}
   284  		temp := scale / smini
   285  		x[0] = temp * b[0]
   286  		x[1] = temp * b[1]
   287  		x[ldb] = temp * b[ldb]
   288  		x[ldb+1] = temp * b[ldb+1]
   289  		xnorm = temp * bnorm
   290  		ok = false
   291  
   292  		return scale, xnorm, ok
   293  	}
   294  
   295  	// Gaussian elimination with complete pivoting.
   296  	ur11 := crv[icmax]
   297  	ui11 := civ[icmax]
   298  	ur12 := crv[pivot[icmax][1]]
   299  	ui12 := civ[pivot[icmax][1]]
   300  	cr21 := crv[pivot[icmax][2]]
   301  	ci21 := civ[pivot[icmax][2]]
   302  	cr22 := crv[pivot[icmax][3]]
   303  	ci22 := civ[pivot[icmax][3]]
   304  	var (
   305  		ur11r, ui11r float64
   306  		lr21, li21   float64
   307  		ur12s, ui12s float64
   308  		ur22, ui22   float64
   309  	)
   310  	if icmax == 0 || icmax == 3 {
   311  		// Off-diagonals of pivoted C are real.
   312  		if math.Abs(ur11) > math.Abs(ui11) {
   313  			temp := ui11 / ur11
   314  			ur11r = 1 / (ur11 * (1 + temp*temp))
   315  			ui11r = -temp * ur11r
   316  		} else {
   317  			temp := ur11 / ui11
   318  			ui11r = -1 / (ui11 * (1 + temp*temp))
   319  			ur11r = -temp * ui11r
   320  		}
   321  		lr21 = cr21 * ur11r
   322  		li21 = cr21 * ui11r
   323  		ur12s = ur12 * ur11r
   324  		ui12s = ur12 * ui11r
   325  		ur22 = cr22 - ur12*lr21
   326  		ui22 = ci22 - ur12*li21
   327  	} else {
   328  		// Diagonals of pivoted C are real.
   329  		ur11r = 1 / ur11
   330  		// ui11r is already 0.
   331  		lr21 = cr21 * ur11r
   332  		li21 = ci21 * ur11r
   333  		ur12s = ur12 * ur11r
   334  		ui12s = ui12 * ur11r
   335  		ur22 = cr22 - ur12*lr21 + ui12*li21
   336  		ui22 = -ur12*li21 - ui12*lr21
   337  	}
   338  	u22abs := math.Abs(ur22) + math.Abs(ui22)
   339  
   340  	// If smaller pivot < smini, use smini.
   341  	if u22abs < smini {
   342  		ur22 = smini
   343  		ui22 = 0
   344  		ok = false
   345  	}
   346  
   347  	var br1, bi1 float64
   348  	var br2, bi2 float64
   349  	if icmax > 1 {
   350  		// If the pivot lies in the second row, swap the rows.
   351  		br1 = b[ldb]
   352  		bi1 = b[ldb+1]
   353  		br2 = b[0]
   354  		bi2 = b[1]
   355  	} else {
   356  		br1 = b[0]
   357  		bi1 = b[1]
   358  		br2 = b[ldb]
   359  		bi2 = b[ldb+1]
   360  	}
   361  	br2 += -lr21*br1 + li21*bi1
   362  	bi2 += -li21*br1 - lr21*bi1
   363  
   364  	bbnd1 := u22abs * (math.Abs(ur11r) + math.Abs(ui11r)) * (math.Abs(br1) + math.Abs(bi1))
   365  	bbnd2 := math.Abs(br2) + math.Abs(bi2)
   366  	bbnd := math.Max(bbnd1, bbnd2)
   367  	if bbnd > 1 && u22abs < 1 && bbnd >= bignum*u22abs {
   368  		scale = 1 / bbnd
   369  		br1 *= scale
   370  		bi1 *= scale
   371  		br2 *= scale
   372  		bi2 *= scale
   373  	}
   374  
   375  	cx2 := complex(br2, bi2) / complex(ur22, ui22)
   376  	xr2, xi2 := real(cx2), imag(cx2)
   377  	xr1 := ur11r*br1 - ui11r*bi1 - ur12s*xr2 + ui12s*xi2
   378  	xi1 := ui11r*br1 + ur11r*bi1 - ui12s*xr2 - ur12s*xi2
   379  	if icmax&0x1 != 0 {
   380  		// If the pivot lies in the second column, swap the components of the solution.
   381  		x[0] = xr2
   382  		x[1] = xi2
   383  		x[ldx] = xr1
   384  		x[ldx+1] = xi1
   385  	} else {
   386  		x[0] = xr1
   387  		x[1] = xi1
   388  		x[ldx] = xr2
   389  		x[ldx+1] = xi2
   390  	}
   391  	xnorm = math.Max(math.Abs(xr1)+math.Abs(xi1), math.Abs(xr2)+math.Abs(xi2))
   392  
   393  	// Further scaling if norm(A)*norm(X) > overflow.
   394  	if xnorm > 1 && cmax > 1 && xnorm > bignum/cmax {
   395  		temp := cmax / bignum
   396  		x[0] *= temp
   397  		x[1] *= temp
   398  		x[ldx] *= temp
   399  		x[ldx+1] *= temp
   400  		xnorm *= temp
   401  		scale *= temp
   402  	}
   403  
   404  	return scale, xnorm, ok
   405  }