Example #1
0
// checkPLU checks that the PLU factorization contained in factorize matches
// the original matrix contained in original.
func checkPLU(t *testing.T, ok bool, m, n, lda int, ipiv []int, factorized, original []float64, tol float64, print bool) {
	var hasZeroDiagonal bool
	for i := 0; i < min(m, n); i++ {
		if factorized[i*lda+i] == 0 {
			hasZeroDiagonal = true
			break
		}
	}
	if hasZeroDiagonal && ok {
		t.Error("Has a zero diagonal but returned ok")
	}
	if !hasZeroDiagonal && !ok {
		t.Error("Non-zero diagonal but returned !ok")
	}

	// Check that the LU decomposition is correct.
	mn := min(m, n)
	l := make([]float64, m*mn)
	ldl := mn
	u := make([]float64, mn*n)
	ldu := n
	for i := 0; i < m; i++ {
		for j := 0; j < n; j++ {
			v := factorized[i*lda+j]
			switch {
			case i == j:
				l[i*ldl+i] = 1
				u[i*ldu+i] = v
			case i > j:
				l[i*ldl+j] = v
			case i < j:
				u[i*ldu+j] = v
			}
		}
	}

	LU := blas64.General{
		Rows:   m,
		Cols:   n,
		Stride: n,
		Data:   make([]float64, m*n),
	}
	U := blas64.General{
		Rows:   mn,
		Cols:   n,
		Stride: ldu,
		Data:   u,
	}
	L := blas64.General{
		Rows:   m,
		Cols:   mn,
		Stride: ldl,
		Data:   l,
	}
	blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, L, U, 0, LU)

	p := make([]float64, m*m)
	ldp := m
	for i := 0; i < m; i++ {
		p[i*ldp+i] = 1
	}
	for i := len(ipiv) - 1; i >= 0; i-- {
		v := ipiv[i]
		blas64.Swap(m, blas64.Vector{1, p[i*ldp:]}, blas64.Vector{1, p[v*ldp:]})
	}
	P := blas64.General{
		Rows:   m,
		Cols:   m,
		Stride: m,
		Data:   p,
	}
	aComp := blas64.General{
		Rows:   m,
		Cols:   n,
		Stride: lda,
		Data:   make([]float64, m*lda),
	}
	copy(aComp.Data, factorized)
	blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, P, LU, 0, aComp)
	if !floats.EqualApprox(aComp.Data, original, tol) {
		if print {
			t.Errorf("PLU multiplication does not match original matrix.\nWant: %v\nGot: %v", original, aComp.Data)
			return
		}
		t.Error("PLU multiplication does not match original matrix.")
	}
}
Example #2
0
func testDgebak(t *testing.T, impl Dgebaker, job lapack.Job, side blas.Side, ilo, ihi int, v blas64.General, rnd *rand.Rand) {
	const tol = 1e-15
	n := v.Rows
	m := v.Cols
	extra := v.Stride - v.Cols

	// Create D and D^{-1} by generating random scales between ilo and ihi.
	d := eye(n, n)
	dinv := eye(n, n)
	scale := nanSlice(n)
	if job == lapack.Scale || job == lapack.PermuteScale {
		if ilo == ihi {
			scale[ilo] = 1
		} else {
			for i := ilo; i <= ihi; i++ {
				scale[i] = 2 * rnd.Float64()
				d.Data[i*d.Stride+i] = scale[i]
				dinv.Data[i*dinv.Stride+i] = 1 / scale[i]
			}
		}
	}

	// Create P by generating random column swaps.
	p := eye(n, n)
	if job == lapack.Permute || job == lapack.PermuteScale {
		// Make up some random permutations.
		for i := n - 1; i > ihi; i-- {
			scale[i] = float64(rnd.Intn(i + 1))
			blas64.Swap(n,
				blas64.Vector{p.Stride, p.Data[i:]},
				blas64.Vector{p.Stride, p.Data[int(scale[i]):]})
		}
		for i := 0; i < ilo; i++ {
			scale[i] = float64(i + rnd.Intn(ihi-i+1))
			blas64.Swap(n,
				blas64.Vector{p.Stride, p.Data[i:]},
				blas64.Vector{p.Stride, p.Data[int(scale[i]):]})
		}
	}

	got := cloneGeneral(v)
	impl.Dgebak(job, side, n, ilo, ihi, scale, m, got.Data, got.Stride)

	prefix := fmt.Sprintf("Case job=%v, side=%v, n=%v, ilo=%v, ihi=%v, m=%v, extra=%v",
		job, side, n, ilo, ihi, m, extra)

	if !generalOutsideAllNaN(got) {
		t.Errorf("%v: out-of-range write to V\n%v", prefix, got.Data)
	}

	// Compute D*V or D^{-1}*V and store into dv.
	dv := zeros(n, m, m)
	if side == blas.Right {
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, d, v, 0, dv)
	} else {
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, dinv, v, 0, dv)
	}
	// Compute P*D*V or P*D^{-1}*V and store into want.
	want := zeros(n, m, m)
	blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, p, dv, 0, want)

	if !equalApproxGeneral(want, got, tol) {
		t.Errorf("%v: unexpected value of V", prefix)
	}
}
Example #3
0
func testDgebal(t *testing.T, impl Dgebaler, job lapack.Job, a blas64.General) {
	const tol = 1e-14

	n := a.Rows
	extra := a.Stride - n

	var scale []float64
	if n > 0 {
		scale = nanSlice(n)
	}

	want := cloneGeneral(a)

	ilo, ihi := impl.Dgebal(job, n, a.Data, a.Stride, scale)

	prefix := fmt.Sprintf("Case job=%v, n=%v, extra=%v", job, n, extra)

	if !generalOutsideAllNaN(a) {
		t.Errorf("%v: out-of-range write to A\n%v", prefix, a.Data)
	}

	if n == 0 {
		if ilo != 0 {
			t.Errorf("%v: unexpected ilo when n=0. Want 0, got %v", prefix, n, ilo)
		}
		if ihi != -1 {
			t.Errorf("%v: unexpected ihi when n=0. Want -1, got %v", prefix, n, ihi)
		}
		return
	}

	if job == lapack.None {
		if ilo != 0 {
			t.Errorf("%v: unexpected ilo when job=None. Want 0, got %v", prefix, ilo)
		}
		if ihi != n-1 {
			t.Errorf("%v: unexpected ihi when job=None. Want %v, got %v", prefix, n-1, ihi)
		}
		k := -1
		for i := range scale {
			if scale[i] != 1 {
				k = i
				break
			}
		}
		if k != -1 {
			t.Errorf("%v: unexpected scale[%v] when job=None. Want 1, got %v", prefix, k, scale[k])
		}
		if !equalApproxGeneral(a, want, 0) {
			t.Errorf("%v: unexpected modification of A when job=None", prefix)
		}
		return
	}

	if ilo < 0 || ihi < ilo || n <= ihi {
		t.Errorf("%v: invalid ordering of ilo=%v and ihi=%v", prefix, ilo, ihi)
	}

	if ilo >= 2 && !isUpperTriangular(blas64.General{ilo - 1, ilo - 1, a.Stride, a.Data}) {
		t.Errorf("%v: T1 is not upper triangular", prefix)
	}
	m := n - ihi - 1 // Order of T2.
	k := ihi + 1
	if m >= 2 && !isUpperTriangular(blas64.General{m, m, a.Stride, a.Data[k*a.Stride+k:]}) {
		t.Errorf("%v: T2 is not upper triangular", prefix)
	}

	if job == lapack.Permute || job == lapack.PermuteScale {
		// Check that all rows in [ilo:ihi+1] have at least one nonzero
		// off-diagonal element.
		zeroRow := -1
		for i := ilo; i <= ihi; i++ {
			onlyZeros := true
			for j := ilo; j <= ihi; j++ {
				if i != j && a.Data[i*a.Stride+j] != 0 {
					onlyZeros = false
					break
				}
			}
			if onlyZeros {
				zeroRow = i
				break
			}
		}
		if zeroRow != -1 && ilo != ihi {
			t.Errorf("%v: row %v has only zero off-diagonal elements, ilo=%v, ihi=%v", prefix, zeroRow, ilo, ihi)
		}
		// Check that all columns in [ilo:ihi+1] have at least one nonzero
		// off-diagonal element.
		zeroCol := -1
		for j := ilo; j <= ihi; j++ {
			onlyZeros := true
			for i := ilo; i <= ihi; i++ {
				if i != j && a.Data[i*a.Stride+j] != 0 {
					onlyZeros = false
					break
				}
			}
			if onlyZeros {
				zeroCol = j
				break
			}
		}
		if zeroCol != -1 && ilo != ihi {
			t.Errorf("%v: column %v has only zero off-diagonal elements, ilo=%v, ihi=%v", prefix, zeroCol, ilo, ihi)
		}

		// Create the permutation matrix P.
		p := eye(n, n)
		for j := n - 1; j > ihi; j-- {
			blas64.Swap(n,
				blas64.Vector{p.Stride, p.Data[j:]},
				blas64.Vector{p.Stride, p.Data[int(scale[j]):]})
		}
		for j := 0; j < ilo; j++ {
			blas64.Swap(n,
				blas64.Vector{p.Stride, p.Data[j:]},
				blas64.Vector{p.Stride, p.Data[int(scale[j]):]})
		}
		// Compute P^T*A*P and store into want.
		ap := zeros(n, n, n)
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, want, p, 0, ap)
		blas64.Gemm(blas.Trans, blas.NoTrans, 1, p, ap, 0, want)
	}
	if job == lapack.Scale || job == lapack.PermuteScale {
		// Modify want by D and D^{-1}.
		d := eye(n, n)
		dinv := eye(n, n)
		for i := ilo; i <= ihi; i++ {
			d.Data[i*d.Stride+i] = scale[i]
			dinv.Data[i*dinv.Stride+i] = 1 / scale[i]
		}
		ad := zeros(n, n, n)
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, want, d, 0, ad)
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, dinv, ad, 0, want)
	}
	if !equalApproxGeneral(want, a, tol) {
		t.Errorf("%v: unexpected value of A, ilo=%v, ihi=%v", prefix, ilo, ihi)
	}
}