Beispiel #1
0
// Inverse computes the inverse of the matrix a, storing the result into the
// receiver. If a is ill-conditioned, a Condition error will be returned.
// Note that matrix inversion is numerically unstable, and should generally
// be avoided where possible, for example by using the Solve routines.
func (m *Dense) Inverse(a Matrix) error {
	// TODO(btracey): Special case for RawTriangular, etc.
	r, c := a.Dims()
	if r != c {
		panic(ErrSquare)
	}
	m.reuseAs(a.Dims())
	aMat, aTrans := untranspose(a)
	if m != aMat {
		m.Copy(a)
	} else if aTrans {
		tmp := getWorkspace(r, c, false)
		tmp.Copy(a)
		m.Copy(tmp)
		putWorkspace(tmp)
	}
	ipiv := make([]int, r)
	lapack64.Getrf(m.mat, ipiv)
	work := make([]float64, 1, 4*r) // must be at least 4*r for cond.
	lapack64.Getri(m.mat, ipiv, work, -1)
	if int(work[0]) > 4*r {
		work = make([]float64, int(work[0]))
	} else {
		work = work[:4*r]
	}
	lapack64.Getri(m.mat, ipiv, work, len(work))
	norm := lapack64.Lange(condNorm, m.mat, work)
	cond := lapack64.Gecon(condNorm, m.mat, norm, work, ipiv) // reuse ipiv
	if cond > condTol {
		return Condition(cond)
	}
	return nil
}
Beispiel #2
0
// Inverse computes the inverse of the matrix a, storing the result into the
// receiver. If a is ill-conditioned, a Condition error will be returned.
// Note that matrix inversion is numerically unstable, and should generally
// be avoided where possible, for example by using the Solve routines.
func (m *Dense) Inverse(a Matrix) error {
	// TODO(btracey): Special case for RawTriangular, etc.
	r, c := a.Dims()
	if r != c {
		panic(matrix.ErrSquare)
	}
	m.reuseAs(a.Dims())
	aU, aTrans := untranspose(a)
	switch rm := aU.(type) {
	case RawMatrixer:
		if m != aU || aTrans {
			if m == aU || m.checkOverlap(rm.RawMatrix()) {
				tmp := getWorkspace(r, c, false)
				tmp.Copy(a)
				m.Copy(tmp)
				putWorkspace(tmp)
				break
			}
			m.Copy(a)
		}
	default:
		if m != aU {
			m.Copy(a)
		} else if aTrans {
			// m and a share data so Copy cannot be used directly.
			tmp := getWorkspace(r, c, false)
			tmp.Copy(a)
			m.Copy(tmp)
			putWorkspace(tmp)
		}
	}
	ipiv := make([]int, r)
	lapack64.Getrf(m.mat, ipiv)
	work := make([]float64, 1, 4*r) // must be at least 4*r for cond.
	lapack64.Getri(m.mat, ipiv, work, -1)
	if int(work[0]) > 4*r {
		work = make([]float64, int(work[0]))
	} else {
		work = work[:4*r]
	}
	lapack64.Getri(m.mat, ipiv, work, len(work))
	norm := lapack64.Lange(matrix.CondNorm, m.mat, work)
	cond := lapack64.Gecon(matrix.CondNorm, m.mat, norm, work, ipiv) // reuse ipiv
	if cond > matrix.ConditionTolerance {
		return matrix.Condition(cond)
	}
	return nil
}
Beispiel #3
0
// updateCond updates the stored condition number of the matrix. Norm is the
// norm of the original matrix. If norm is negative it will be estimated.
func (lu *LU) updateCond(norm float64) {
	n := lu.lu.mat.Cols
	work := make([]float64, 4*n)
	iwork := make([]int, n)
	if norm < 0 {
		// This is an approximation. By the definition of a norm, ||AB|| <= ||A|| ||B||.
		// The condition number is ||A|| || A^-1||, so this will underestimate
		// the condition number somewhat.
		// The norm of the original factorized matrix cannot be stored because of
		// update possibilities, e.g. RankOne.
		u := lu.lu.asTriDense(n, blas.NonUnit, blas.Upper)
		l := lu.lu.asTriDense(n, blas.Unit, blas.Lower)
		unorm := lapack64.Lantr(matrix.CondNorm, u.mat, work)
		lnorm := lapack64.Lantr(matrix.CondNorm, l.mat, work)
		norm = unorm * lnorm
	}
	v := lapack64.Gecon(matrix.CondNorm, lu.lu.mat, norm, work, iwork)
	lu.cond = 1 / v
}
Beispiel #4
0
// Cond returns the condition number of the given matrix under the given norm.
// The condition number must be based on the 1-norm, 2-norm or ∞-norm.
// Cond will panic with matrix.ErrShape if the matrix has zero size.
//
// BUG(btracey): The computation of the 1-norm and ∞-norm for non-square matrices
// is innacurate, although is typically the right order of magnitude. See
// https://github.com/xianyi/OpenBLAS/issues/636. While the value returned will
// change with the resolution of this bug, the result from Cond will match the
// condition number used internally.
func Cond(a Matrix, norm float64) float64 {
	m, n := a.Dims()
	if m == 0 || n == 0 {
		panic(matrix.ErrShape)
	}
	var lnorm lapack.MatrixNorm
	switch norm {
	default:
		panic("mat64: bad norm value")
	case 1:
		lnorm = lapack.MaxColumnSum
	case 2:
		var svd SVD
		ok := svd.Factorize(a, matrix.SVDNone)
		if !ok {
			return math.Inf(1)
		}
		return svd.Cond()
	case math.Inf(1):
		lnorm = lapack.MaxRowSum
	}
	if m == n {
		// Use the LU decomposition to compute the condition number.
		tmp := getWorkspace(m, n, false)
		tmp.Copy(a)
		work := make([]float64, 4*n)
		aNorm := lapack64.Lange(lnorm, tmp.mat, work)
		pivot := make([]int, m)
		lapack64.Getrf(tmp.mat, pivot)
		iwork := make([]int, n)
		v := lapack64.Gecon(lnorm, tmp.mat, aNorm, work, iwork)
		putWorkspace(tmp)
		return 1 / v
	}
	if m > n {
		// Use the QR factorization to compute the condition number.
		tmp := getWorkspace(m, n, false)
		tmp.Copy(a)
		work := make([]float64, 3*n)
		tau := make([]float64, min(m, n))
		lapack64.Geqrf(tmp.mat, tau, work, -1)
		if int(work[0]) > len(work) {
			work = make([]float64, int(work[0]))
		}
		lapack64.Geqrf(tmp.mat, tau, work, len(work))

		iwork := make([]int, n)
		r := tmp.asTriDense(n, blas.NonUnit, blas.Upper)
		v := lapack64.Trcon(lnorm, r.mat, work, iwork)
		putWorkspace(tmp)
		return 1 / v
	}
	// Use the LQ factorization to compute the condition number.
	tmp := getWorkspace(m, n, false)
	tmp.Copy(a)
	work := make([]float64, 3*m)
	tau := make([]float64, min(m, n))
	lapack64.Gelqf(tmp.mat, tau, work, -1)
	if int(work[0]) > len(work) {
		work = make([]float64, int(work[0]))
	}
	lapack64.Gelqf(tmp.mat, tau, work, len(work))

	iwork := make([]int, m)
	l := tmp.asTriDense(m, blas.NonUnit, blas.Lower)
	v := lapack64.Trcon(lnorm, l.mat, work, iwork)
	putWorkspace(tmp)
	return 1 / v
}
Beispiel #5
0
// Cond returns the condition number of the given matrix under the given norm.
// The condition number must be based on the 1-norm, 2-norm or ∞-norm.
// Cond will panic with matrix.ErrShape if the matrix has zero size.
//
// BUG(btracey): The computation of the 1-norm and ∞-norm for non-square matrices
// is innacurate, although is typically the right order of magnitude. See
// https://github.com/xianyi/OpenBLAS/issues/636. While the value returned will
// change with the resolution of this bug, the result from Cond will match the
// condition number used internally.
func Cond(a Matrix, norm float64) float64 {
	m, n := a.Dims()
	if m == 0 || n == 0 {
		panic(matrix.ErrShape)
	}
	var lnorm lapack.MatrixNorm
	switch norm {
	default:
		panic("mat64: bad norm value")
	case 1:
		lnorm = lapack.MaxColumnSum
	case 2:
		// Use SVD to compute the condition number.
		// TODO(btracey): Replace this with temporary workspace when SVD is fixed.
		tmp := NewDense(m, n, nil)
		tmp.Copy(a)
		svd := SVD(tmp, math.Pow(2, -52.0), math.Pow(2, -966.0), false, false)
		return svd.Cond()
	case math.Inf(1):
		lnorm = lapack.MaxRowSum
	}
	if m == n {
		// Use the LU decomposition to compute the condition number.
		tmp := getWorkspace(m, n, false)
		tmp.Copy(a)
		work := make([]float64, 4*n)
		aNorm := lapack64.Lange(lnorm, tmp.mat, work)
		pivot := make([]int, m)
		lapack64.Getrf(tmp.mat, pivot)
		iwork := make([]int, n)
		v := lapack64.Gecon(lnorm, tmp.mat, aNorm, work, iwork)
		putWorkspace(tmp)
		return 1 / v
	}
	if m > n {
		// Use the QR factorization to compute the condition number.
		tmp := getWorkspace(m, n, false)
		tmp.Copy(a)
		work := make([]float64, 3*n)
		tau := make([]float64, min(m, n))
		lapack64.Geqrf(tmp.mat, tau, work, -1)
		if int(work[0]) > len(work) {
			work = make([]float64, int(work[0]))
		}
		lapack64.Geqrf(tmp.mat, tau, work, len(work))

		iwork := make([]int, n)
		r := tmp.asTriDense(n, blas.NonUnit, blas.Upper)
		v := lapack64.Trcon(lnorm, r.mat, work, iwork)
		putWorkspace(tmp)
		return 1 / v
	}
	// Use the LQ factorization to compute the condition number.
	tmp := getWorkspace(m, n, false)
	tmp.Copy(a)
	work := make([]float64, 3*m)
	tau := make([]float64, min(m, n))
	lapack64.Gelqf(tmp.mat, tau, work, -1)
	if int(work[0]) > len(work) {
		work = make([]float64, int(work[0]))
	}
	lapack64.Gelqf(tmp.mat, tau, work, len(work))

	iwork := make([]int, m)
	l := tmp.asTriDense(m, blas.NonUnit, blas.Lower)
	v := lapack64.Trcon(lnorm, l.mat, work, iwork)
	putWorkspace(tmp)
	return 1 / v
}