示例#1
0
文件: addressing.go 项目: philipz/ntm
func newWrittenMemory(ws []*refocus, heads []*Head, mtm1 *writtenMemory) *writtenMemory {
	n := mtm1.N
	m := len(mtm1.TopVal) / n
	wm := writtenMemory{
		Ws:    ws,
		Heads: heads,
		Mtm1:  mtm1,

		N:       mtm1.N,
		TopVal:  make([]float64, len(mtm1.TopVal)),
		TopGrad: make([]float64, len(mtm1.TopVal)),

		erase:    makeTensor2(len(heads), m),
		add:      makeTensor2(len(heads), m),
		erasures: make([]float64, len(mtm1.TopVal)),
	}
	for i, h := range wm.Heads {
		erase := wm.erase[i]
		add := wm.add[i]
		addVec := h.AddVal()
		for j, e := range h.EraseVal() {
			erase[j] = Sigmoid(e)
			add[j] = Sigmoid(addVec[j])
		}
	}

	copy(wm.erasures, mtm1.TopVal)
	we := make([]float64, n*m)
	weG := blas64.General{Rows: n, Cols: m, Stride: m, Data: we}
	for k, ws := range wm.Ws {
		weights := blas64.Vector{Inc: 1, Data: ws.TopVal}
		erase := blas64.Vector{Inc: 1, Data: wm.erase[k]}
		for i := range we {
			we[i] = 1
		}
		blas64.Ger(-1, weights, erase, weG)
		floats.Mul(wm.erasures, we)
	}

	copy(wm.TopVal, wm.erasures)
	topG := blas64.General{Rows: n, Cols: m, Stride: m, Data: wm.TopVal}
	for k, ws := range wm.Ws {
		weights := blas64.Vector{Inc: 1, Data: ws.TopVal}
		add := blas64.Vector{Inc: 1, Data: wm.add[k]}
		blas64.Ger(1, weights, add, topG)
	}

	return &wm
}
示例#2
0
文件: addressing.go 项目: philipz/ntm
func (wm *writtenMemory) backwardWErase() {
	n := wm.N
	m := len(wm.TopVal) / n

	mgrad := make([]float64, n*m)
	mGradG := blas64.General{Rows: n, Cols: m, Stride: m, Data: mgrad}
	hEraseGrad := blas64.Vector{Inc: 1, Data: make([]float64, m)}
	for i, weights := range wm.Ws {
		erase := wm.erase[i]
		add := wm.add[i]
		eraseV := blas64.Vector{Inc: 1, Data: erase}
		addV := blas64.Vector{Inc: 1, Data: add}
		weightsVal := blas64.Vector{Inc: 1, Data: weights.TopVal}

		for j := range mgrad {
			mgrad[j] = 0
		}
		blas64.Ger(1, weightsVal, eraseV, mGradG)
		wm.div1MWE(mgrad)
		floats.Mul(mgrad, wm.TopGrad)

		weightsV := blas64.Vector{Inc: 1, Data: weights.TopGrad}
		blas64.Gemv(blas.NoTrans, -1, mGradG, eraseV, 1, weightsV)
		blas64.Gemv(blas.NoTrans, 1, blas64.General{Rows: n, Cols: m, Stride: m, Data: wm.TopGrad}, addV, 1, weightsV)

		hErase := wm.Heads[i].EraseGrad()
		for j := range hEraseGrad.Data {
			hEraseGrad.Data[j] = 0
		}
		blas64.Gemv(blas.Trans, -1, mGradG, weightsVal, 1, hEraseGrad)
		for j, e := range erase {
			hErase[j] += hEraseGrad.Data[j] * e * (1 - e)
		}
	}
}
示例#3
0
文件: general.go 项目: jacobxk/lapack
func constructH(tau []float64, v blas64.General, store lapack.StoreV, direct lapack.Direct) blas64.General {
	m := v.Rows
	k := v.Cols
	if store == lapack.RowWise {
		m, k = k, m
	}
	h := blas64.General{
		Rows:   m,
		Cols:   m,
		Stride: m,
		Data:   make([]float64, m*m),
	}
	for i := 0; i < m; i++ {
		h.Data[i*m+i] = 1
	}
	for i := 0; i < k; i++ {
		vecData := make([]float64, m)
		if store == lapack.ColumnWise {
			for j := 0; j < m; j++ {
				vecData[j] = v.Data[j*v.Cols+i]
			}
		} else {
			for j := 0; j < m; j++ {
				vecData[j] = v.Data[i*v.Cols+j]
			}
		}
		vec := blas64.Vector{
			Inc:  1,
			Data: vecData,
		}

		hi := blas64.General{
			Rows:   m,
			Cols:   m,
			Stride: m,
			Data:   make([]float64, m*m),
		}
		for i := 0; i < m; i++ {
			hi.Data[i*m+i] = 1
		}
		// hi = I - tau * v * v^T
		blas64.Ger(-tau[i], vec, vec, hi)

		hcopy := blas64.General{
			Rows:   m,
			Cols:   m,
			Stride: m,
			Data:   make([]float64, m*m),
		}
		copy(hcopy.Data, h.Data)
		if direct == lapack.Forward {
			// H = H * H_I in forward mode
			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hcopy, hi, 0, h)
		} else {
			// H = H_I * H in backward mode
			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hi, hcopy, 0, h)
		}
	}
	return h
}
// RankOne performs a rank-one update to the matrix b and stores the result
// in the receiver
//  m = a + alpha * x * y'
func (m *Dense) RankOne(a Matrix, alpha float64, x, y []float64) {
	ar, ac := a.Dims()

	var w Dense
	if m == a {
		w = *m
	}
	if w.isZero() {
		w.mat = blas64.General{
			Rows:   ar,
			Cols:   ac,
			Stride: ac,
			Data:   use(w.mat.Data, ar*ac),
		}
	} else if ar != w.mat.Rows || ac != w.mat.Cols {
		panic(ErrShape)
	}
	// Copy over to the new memory if necessary
	if m != a {
		w.Copy(a)
	}
	if len(x) != ar {
		panic(ErrShape)
	}
	if len(y) != ac {
		panic(ErrShape)
	}
	blas64.Ger(alpha, blas64.Vector{Inc: 1, Data: x}, blas64.Vector{Inc: 1, Data: y}, w.mat)
	*m = w
	return
}
示例#5
0
// Outer calculates the outer product of x and y, and stores the result
// in the receiver. In order to update to an existing matrix, see RankOne.
//  m = x * y'
func (m *Dense) Outer(x, y *Vector) {
	r := x.Len()
	c := y.Len()

	// Copied from reuseAs with use replaced by useZeroed
	// and a final zero of the matrix elements if we pass
	// the shape checks.
	// TODO(kortschak): Factor out into reuseZeroedAs if
	// we find another case that needs it.
	if m.mat.Rows > m.capRows || m.mat.Cols > m.capCols {
		// Panic as a string, not a mat64.Error.
		panic("mat64: caps not correctly set")
	}
	if m.isZero() {
		m.mat = blas64.General{
			Rows:   r,
			Cols:   c,
			Stride: c,
			Data:   useZeroed(m.mat.Data, r*c),
		}
		m.capRows = r
		m.capCols = c
	} else if r != m.mat.Rows || c != m.mat.Cols {
		panic(ErrShape)
	} else {
		for i := 0; i < r; i++ {
			zero(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c])
		}
	}

	blas64.Ger(1, x.mat, y.mat, m.mat)
}
示例#6
0
// RankOne performs a rank-one update to the matrix a and stores the result
// in the receiver. If a is zero, see Outer.
//  m = a + alpha * x * y'
func (m *Dense) RankOne(a Matrix, alpha float64, x, y *Vector) {
	ar, ac := a.Dims()
	if x.Len() != ar {
		panic(matrix.ErrShape)
	}
	if y.Len() != ac {
		panic(matrix.ErrShape)
	}

	m.checkOverlap(x.asGeneral())
	m.checkOverlap(y.asGeneral())

	var w Dense
	if m == a {
		w = *m
	}
	w.reuseAs(ar, ac)

	// Copy over to the new memory if necessary
	if m != a {
		w.Copy(a)
	}
	blas64.Ger(alpha, x.mat, y.mat, w.mat)
	*m = w
}
示例#7
0
func testDorghr(t *testing.T, impl Dorghrer, n, ilo, ihi, extra int, optwork bool, rnd *rand.Rand) {
	const tol = 1e-14

	// Construct the matrix A with elementary reflectors and scalar factors tau.
	a := randomGeneral(n, n, n+extra, rnd)
	var tau []float64
	if n > 1 {
		tau = nanSlice(n - 1)
	}
	work := nanSlice(max(1, n)) // Minimum work for Dgehrd.
	impl.Dgehrd(n, ilo, ihi, a.Data, a.Stride, tau, work, len(work))

	// Extract Q for later comparison.
	q := eye(n, n)
	qCopy := cloneGeneral(q)
	for j := ilo; j < ihi; j++ {
		h := eye(n, n)
		v := blas64.Vector{
			Inc:  1,
			Data: make([]float64, n),
		}
		v.Data[j+1] = 1
		for i := j + 2; i < ihi+1; i++ {
			v.Data[i] = a.Data[i*a.Stride+j]
		}
		blas64.Ger(-tau[j], v, v, h)
		copy(qCopy.Data, q.Data)
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, h, 0, q)
	}

	if optwork {
		work = nanSlice(1)
		impl.Dorghr(n, ilo, ihi, a.Data, a.Stride, tau, work, -1)
		work = nanSlice(int(work[0]))
	} else {
		work = nanSlice(max(1, ihi-ilo))
	}
	impl.Dorghr(n, ilo, ihi, a.Data, a.Stride, tau, work, len(work))

	prefix := fmt.Sprintf("Case n=%v, ilo=%v, ihi=%v, extra=%v, optwork=%v", n, ilo, ihi, extra, optwork)
	if !generalOutsideAllNaN(a) {
		t.Errorf("%v: out-of-range write to A\n%v", prefix, a.Data)
	}
	if !isOrthonormal(a) {
		t.Errorf("%v: A is not orthogonal\n%v", prefix, a.Data)
	}
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			aij := a.Data[i*a.Stride+j]
			qij := q.Data[i*q.Stride+j]
			if math.Abs(aij-qij) > tol {
				t.Errorf("%v: unexpected value of A[%v,%v]. want %v, got %v", prefix, i, j, qij, aij)
			}
		}
	}
}
示例#8
0
文件: cntl1.go 项目: philipz/ntm
func (c *controller1) Backward() {
	out := blas64.Vector{Inc: 1, Data: c.outGrad}
	h1Val := blas64.Vector{Inc: 1, Data: c.H1Val}
	h1Grad := blas64.Vector{Inc: 1, Data: c.H1Grad}
	blas64.Gemv(blas.Trans, 1, c.wyVal(), out, 1, h1Grad)
	blas64.Ger(1, out, h1Val, c.wyGrad())

	h1Val = blas64.Vector{Inc: 1, Data: c.H1Val[0:c.h1Size]}
	h1Grad = blas64.Vector{Inc: 1, Data: c.H1Grad[0:c.h1Size]}
	for i, v := range h1Val.Data {
		h1Grad.Data[i] *= v * (1 - v)
	}

	u := blas64.Vector{Inc: 1, Data: make([]float64, c.wh1Cols())}
	blas64.Gemv(blas.Trans, 1, c.wh1Val(), h1Grad, 1, u)
	blas64.Ger(1, h1Grad, c.ReadsXVal, c.wh1Grad())

	for i, read := range c.Reads {
		copy(read.TopGrad, u.Data[i*c.memoryM:(i+1)*c.memoryM])
	}
}
示例#9
0
文件: lq.go 项目: yonglehou/matrix
// QFromLQ extracts the n×n orthonormal matrix Q from an LQ decomposition.
func (m *Dense) QFromLQ(lq *LQ) {
	r, c := lq.lq.Dims()
	m.reuseAs(c, c)

	// Set Q = I.
	for i := 0; i < c; i++ {
		for j := 0; j < i; j++ {
			m.mat.Data[i*m.mat.Stride+j] = 0
		}
		m.mat.Data[i*m.mat.Stride+i] = 1
		for j := i + 1; j < c; j++ {
			m.mat.Data[i*m.mat.Stride+j] = 0
		}
	}

	// Construct Q from the elementary reflectors.
	h := blas64.General{
		Rows:   c,
		Cols:   c,
		Stride: c,
		Data:   make([]float64, c*c),
	}
	qCopy := getWorkspace(c, c, false)
	v := blas64.Vector{
		Inc:  1,
		Data: make([]float64, c),
	}
	for i := 0; i < r; i++ {
		// Set h = I.
		for i := range h.Data {
			h.Data[i] = 0
		}
		for j := 0; j < c; j++ {
			h.Data[j*c+j] = 1
		}

		// Set the vector data as the elementary reflector.
		for j := 0; j < i; j++ {
			v.Data[j] = 0
		}
		v.Data[i] = 1
		for j := i + 1; j < c; j++ {
			v.Data[j] = lq.lq.mat.Data[i*lq.lq.mat.Stride+j]
		}

		// Compute the multiplication matrix.
		blas64.Ger(-lq.tau[i], v, v, h)
		qCopy.Copy(m)
		blas64.Gemm(blas.NoTrans, blas.NoTrans,
			1, h, qCopy.mat,
			0, m.mat)
	}
}
示例#10
0
文件: qr.go 项目: yonglehou/matrix
// QFromQR extracts the m×m orthonormal matrix Q from a QR decomposition.
func (m *Dense) QFromQR(qr *QR) {
	r, c := qr.qr.Dims()
	m.reuseAs(r, r)

	// Set Q = I.
	for i := 0; i < r; i++ {
		for j := 0; j < i; j++ {
			m.mat.Data[i*m.mat.Stride+j] = 0
		}
		m.mat.Data[i*m.mat.Stride+i] = 1
		for j := i + 1; j < r; j++ {
			m.mat.Data[i*m.mat.Stride+j] = 0
		}
	}

	// Construct Q from the elementary reflectors.
	h := blas64.General{
		Rows:   r,
		Cols:   r,
		Stride: r,
		Data:   make([]float64, r*r),
	}
	qCopy := getWorkspace(r, r, false)
	v := blas64.Vector{
		Inc:  1,
		Data: make([]float64, r),
	}
	for i := 0; i < c; i++ {
		// Set h = I.
		for i := range h.Data {
			h.Data[i] = 0
		}
		for j := 0; j < r; j++ {
			h.Data[j*r+j] = 1
		}

		// Set the vector data as the elementary reflector.
		for j := 0; j < i; j++ {
			v.Data[j] = 0
		}
		v.Data[i] = 1
		for j := i + 1; j < r; j++ {
			v.Data[j] = qr.qr.mat.Data[j*qr.qr.mat.Stride+i]
		}

		// Compute the multiplication matrix.
		blas64.Ger(-qr.tau[i], v, v, h)
		qCopy.Copy(m)
		blas64.Gemm(blas.NoTrans, blas.NoTrans,
			1, qCopy.mat, h,
			0, m.mat)
	}
}
示例#11
0
文件: addressing.go 项目: philipz/ntm
func (r *memRead) Backward() {
	n := r.Memory.N
	m := len(r.Memory.TopVal) / n

	grad := blas64.Vector{Inc: 1, Data: r.TopGrad}
	memVal := blas64.General{Rows: n, Cols: m, Stride: m, Data: r.Memory.TopVal}
	weightsGrad := blas64.Vector{Inc: 1, Data: r.W.TopGrad}
	blas64.Gemv(blas.NoTrans, 1, memVal, grad, 1, weightsGrad)

	memGrad := blas64.General{Rows: n, Cols: m, Stride: m, Data: r.Memory.TopGrad}
	weights := blas64.Vector{Inc: 1, Data: r.W.TopVal}
	blas64.Ger(1, weights, grad, memGrad)
}
示例#12
0
func testDlarfx(t *testing.T, impl Dlarfxer, side blas.Side, m, n, extra int, rnd *rand.Rand) {
	const tol = 1e-13

	c := randomGeneral(m, n, n+extra, rnd)
	cWant := randomGeneral(m, n, n+extra, rnd)
	tau := rnd.NormFloat64()

	var (
		v []float64
		h blas64.General
	)
	if side == blas.Left {
		v = randomSlice(m, rnd)
		h = eye(m, m+extra)
	} else {
		v = randomSlice(n, rnd)
		h = eye(n, n+extra)
	}
	blas64.Ger(-tau, blas64.Vector{Inc: 1, Data: v}, blas64.Vector{Inc: 1, Data: v}, h)
	if side == blas.Left {
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, h, c, 0, cWant)
	} else {
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, c, h, 0, cWant)
	}

	var work []float64
	if h.Rows > 10 {
		// Allocate work only if H has order > 10.
		if side == blas.Left {
			work = make([]float64, n)
		} else {
			work = make([]float64, m)
		}
	}

	impl.Dlarfx(side, m, n, v, tau, c.Data, c.Stride, work)

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

	// Check any invalid modifications of c.
	if !generalOutsideAllNaN(c) {
		t.Errorf("%v: out-of-range write to C\n%v", prefix, c.Data)
	}

	if !equalApproxGeneral(c, cWant, tol) {
		t.Errorf("%v: unexpected C\n%v", prefix, c.Data)
	}
}
示例#13
0
// RankOne performs a rank-one update to the matrix a and stores the result
// in the receiver. If a is zero, see Outer.
//  m = a + alpha * x * y'
func (m *Dense) RankOne(a Matrix, alpha float64, x, y *Vector) {
	ar, ac := a.Dims()
	if x.Len() != ar {
		panic(ErrShape)
	}
	if y.Len() != ac {
		panic(ErrShape)
	}

	var w Dense
	if m == a {
		w = *m
	}
	w.reuseAs(ar, ac)

	// Copy over to the new memory if necessary
	if m != a {
		w.Copy(a)
	}
	blas64.Ger(alpha, x.Mat, y.Mat, w.Mat)
	*m = w
}
示例#14
0
// RankOne performs a rank-one update to the matrix b and stores the result
// in the receiver
//  m = a + alpha * x * y'
func (m *Dense) RankOne(a Matrix, alpha float64, x, y []float64) {
	ar, ac := a.Dims()

	var w Dense
	if m == a {
		w = *m
	}
	w.reuseAs(ar, ac)

	// Copy over to the new memory if necessary
	if m != a {
		w.Copy(a)
	}
	if len(x) != ar {
		panic(ErrShape)
	}
	if len(y) != ac {
		panic(ErrShape)
	}
	blas64.Ger(alpha, blas64.Vector{Inc: 1, Data: x}, blas64.Vector{Inc: 1, Data: y}, w.mat)
	*m = w
	return
}
示例#15
0
func DlarfTest(t *testing.T, impl Dlarfer) {
	for i, test := range []struct {
		m, n, ldc    int
		incv, lastv  int
		lastr, lastc int
		tau          float64
	}{
		{
			m:   3,
			n:   2,
			ldc: 2,

			incv:  4,
			lastv: 1,

			lastr: 2,
			lastc: 1,

			tau: 2,
		},
		{
			m:   2,
			n:   3,
			ldc: 3,

			incv:  4,
			lastv: 1,

			lastr: 1,
			lastc: 2,

			tau: 2,
		},
		{
			m:   2,
			n:   3,
			ldc: 3,

			incv:  4,
			lastv: 1,

			lastr: 0,
			lastc: 1,

			tau: 2,
		},
		{
			m:   2,
			n:   3,
			ldc: 3,

			incv:  4,
			lastv: 0,

			lastr: 0,
			lastc: 1,

			tau: 2,
		},
		{
			m:   10,
			n:   10,
			ldc: 10,

			incv:  4,
			lastv: 6,

			lastr: 9,
			lastc: 8,

			tau: 2,
		},
	} {
		// Construct a random matrix.
		c := make([]float64, test.ldc*test.m)
		for i := 0; i <= test.lastr; i++ {
			for j := 0; j <= test.lastc; j++ {
				c[i*test.ldc+j] = rand.Float64()
			}
		}
		cCopy := make([]float64, len(c))
		copy(cCopy, c)
		cCopy2 := make([]float64, len(c))
		copy(cCopy2, c)

		// Test with side right.
		sz := max(test.m, test.n) // so v works for both right and left side.
		v := make([]float64, test.incv*sz+1)
		// Fill with nonzero entries up until lastv.
		for i := 0; i <= test.lastv; i++ {
			v[i*test.incv] = rand.Float64()
		}
		// Construct h explicitly to compare.
		h := make([]float64, test.n*test.n)
		for i := 0; i < test.n; i++ {
			h[i*test.n+i] = 1
		}
		hMat := blas64.General{
			Rows:   test.n,
			Cols:   test.n,
			Stride: test.n,
			Data:   h,
		}
		vVec := blas64.Vector{
			Inc:  test.incv,
			Data: v,
		}
		blas64.Ger(-test.tau, vVec, vVec, hMat)

		// Apply multiplication (2nd copy is to avoid aliasing).
		cMat := blas64.General{
			Rows:   test.m,
			Cols:   test.n,
			Stride: test.ldc,
			Data:   cCopy,
		}
		cMat2 := blas64.General{
			Rows:   test.m,
			Cols:   test.n,
			Stride: test.ldc,
			Data:   cCopy2,
		}
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, cMat2, hMat, 0, cMat)

		// cMat now stores the true answer. Compare with the function call.
		work := make([]float64, sz)
		impl.Dlarf(blas.Right, test.m, test.n, v, test.incv, test.tau, c, test.ldc, work)
		if !floats.EqualApprox(c, cMat.Data, 1e-14) {
			t.Errorf("Dlarf mismatch right, case %v. Want %v, got %v", i, cMat.Data, c)
		}

		// Test on the left side.
		copy(c, cCopy2)
		copy(cCopy, c)
		// Construct h.
		h = make([]float64, test.m*test.m)
		for i := 0; i < test.m; i++ {
			h[i*test.m+i] = 1
		}
		hMat = blas64.General{
			Rows:   test.m,
			Cols:   test.m,
			Stride: test.m,
			Data:   h,
		}
		blas64.Ger(-test.tau, vVec, vVec, hMat)
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hMat, cMat2, 0, cMat)
		impl.Dlarf(blas.Left, test.m, test.n, v, test.incv, test.tau, c, test.ldc, work)
		if !floats.EqualApprox(c, cMat.Data, 1e-14) {
			t.Errorf("Dlarf mismatch left, case %v. Want %v, got %v", i, cMat.Data, c)
		}
	}
}
示例#16
0
func testDgehd2(t *testing.T, impl Dgehd2er, n, extra int, rnd *rand.Rand) {
	ilo := rnd.Intn(n)
	ihi := rnd.Intn(n)
	if ilo > ihi {
		ilo, ihi = ihi, ilo
	}

	tau := nanSlice(n - 1)
	work := nanSlice(n)

	a := randomGeneral(n, n, n+extra, rnd)
	// NaN out elements under the diagonal except
	// for the [ilo:ihi,ilo:ihi] block.
	for i := 1; i <= ihi; i++ {
		for j := 0; j < min(ilo, i); j++ {
			a.Data[i*a.Stride+j] = math.NaN()
		}
	}
	for i := ihi + 1; i < n; i++ {
		for j := 0; j < i; j++ {
			a.Data[i*a.Stride+j] = math.NaN()
		}
	}
	aCopy := a
	aCopy.Data = make([]float64, len(a.Data))
	copy(aCopy.Data, a.Data)

	impl.Dgehd2(n, ilo, ihi, a.Data, a.Stride, tau, work)

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

	// Check any invalid modifications of a.
	if !generalOutsideAllNaN(a) {
		t.Errorf("%v: out-of-range write to A\n%v", prefix, a.Data)
	}
	for i := ilo; i <= ihi; i++ {
		for j := 0; j < min(ilo, i); j++ {
			if !math.IsNaN(a.Data[i*a.Stride+j]) {
				t.Errorf("%v: expected NaN at A[%v,%v]", prefix, i, j)
			}
		}
	}
	for i := ihi + 1; i < n; i++ {
		for j := 0; j < i; j++ {
			if !math.IsNaN(a.Data[i*a.Stride+j]) {
				t.Errorf("%v: expected NaN at A[%v,%v]", prefix, i, j)
			}
		}
	}
	for i := 0; i <= ilo; i++ {
		for j := i; j < ilo+1; j++ {
			if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] {
				t.Errorf("%v: unexpected modification at A[%v,%v]", prefix, i, j)
			}
		}
		for j := ihi + 1; j < n; j++ {
			if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] {
				t.Errorf("%v: unexpected modification at A[%v,%v]", prefix, i, j)
			}
		}
	}
	for i := ihi + 1; i < n; i++ {
		for j := i; j < n; j++ {
			if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] {
				t.Errorf("%v: unexpected modification at A[%v,%v]", prefix, i, j)
			}
		}
	}

	// Check that tau has been assigned properly.
	for i, v := range tau {
		if i < ilo || i >= ihi {
			if !math.IsNaN(v) {
				t.Errorf("%v: expected NaN at tau[%v]", prefix, i)
			}
		} else {
			if math.IsNaN(v) {
				t.Errorf("%v: unexpected NaN at tau[%v]", prefix, i)
			}
		}
	}

	// Extract Q and check that it is orthogonal.
	q := blas64.General{
		Rows:   n,
		Cols:   n,
		Stride: n,
		Data:   make([]float64, n*n),
	}
	for i := 0; i < q.Rows; i++ {
		q.Data[i*q.Stride+i] = 1
	}
	qCopy := q
	qCopy.Data = make([]float64, len(q.Data))
	for j := ilo; j < ihi; j++ {
		h := blas64.General{
			Rows:   n,
			Cols:   n,
			Stride: n,
			Data:   make([]float64, n*n),
		}
		for i := 0; i < h.Rows; i++ {
			h.Data[i*h.Stride+i] = 1
		}
		v := blas64.Vector{
			Inc:  1,
			Data: make([]float64, n),
		}
		v.Data[j+1] = 1
		for i := j + 2; i < ihi+1; i++ {
			v.Data[i] = a.Data[i*a.Stride+j]
		}
		blas64.Ger(-tau[j], v, v, h)
		copy(qCopy.Data, q.Data)
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, h, 0, q)
	}
	if !isOrthonormal(q) {
		t.Errorf("%v: Q is not orthogonal\nQ=%v", prefix, q)
	}

	// Overwrite NaN elements of aCopy with zeros
	// (we will multiply with it below).
	for i := 1; i <= ihi; i++ {
		for j := 0; j < min(ilo, i); j++ {
			aCopy.Data[i*aCopy.Stride+j] = 0
		}
	}
	for i := ihi + 1; i < n; i++ {
		for j := 0; j < i; j++ {
			aCopy.Data[i*aCopy.Stride+j] = 0
		}
	}

	// Construct Q^T * AOrig * Q and check that it is
	// equal to A from Dgehd2.
	aq := blas64.General{
		Rows:   n,
		Cols:   n,
		Stride: n,
		Data:   make([]float64, n*n),
	}
	blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, aCopy, q, 0, aq)
	qaq := blas64.General{
		Rows:   n,
		Cols:   n,
		Stride: n,
		Data:   make([]float64, n*n),
	}
	blas64.Gemm(blas.Trans, blas.NoTrans, 1, q, aq, 0, qaq)
	for i := ilo; i <= ihi; i++ {
		for j := ilo; j <= ihi; j++ {
			qaqij := qaq.Data[i*qaq.Stride+j]
			if j < i-1 {
				if math.Abs(qaqij) > 1e-14 {
					t.Errorf("%v: Q^T*A*Q is not upper Hessenberg, [%v,%v]=%v", prefix, i, j, qaqij)
				}
				continue
			}
			diff := qaqij - a.Data[i*a.Stride+j]
			if math.Abs(diff) > 1e-14 {
				t.Errorf("%v: Q^T*AOrig*Q and A are not equal, diff at [%v,%v]=%v", prefix, i, j, diff)
			}
		}
	}
}
示例#17
0
func Dgeql2Test(t *testing.T, impl Dgeql2er) {
	rnd := rand.New(rand.NewSource(1))
	// TODO(btracey): Add tests for m < n.
	for _, test := range []struct {
		m, n, lda int
	}{
		{5, 5, 0},
		{5, 3, 0},
		{5, 4, 0},
	} {
		m := test.m
		n := test.n
		lda := test.lda
		if lda == 0 {
			lda = n
		}
		a := make([]float64, m*lda)
		for i := range a {
			a[i] = rnd.NormFloat64()
		}
		tau := nanSlice(min(m, n))
		work := nanSlice(n)

		aCopy := make([]float64, len(a))
		copy(aCopy, a)
		impl.Dgeql2(m, n, a, lda, tau, work)

		k := min(m, n)
		// Construct Q.
		q := blas64.General{
			Rows:   m,
			Cols:   m,
			Stride: m,
			Data:   make([]float64, m*m),
		}
		for i := 0; i < m; i++ {
			q.Data[i*q.Stride+i] = 1
		}
		for i := 0; i < k; i++ {
			h := blas64.General{Rows: m, Cols: m, Stride: m, Data: make([]float64, m*m)}
			for j := 0; j < m; j++ {
				h.Data[j*h.Stride+j] = 1
			}
			v := blas64.Vector{Inc: 1, Data: make([]float64, m)}
			v.Data[m-k+i] = 1
			for j := 0; j < m-k+i; j++ {
				v.Data[j] = a[j*lda+n-k+i]
			}
			blas64.Ger(-tau[i], v, v, h)
			qTmp := blas64.General{Rows: q.Rows, Cols: q.Cols, Stride: q.Stride, Data: make([]float64, len(q.Data))}
			copy(qTmp.Data, q.Data)
			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, h, qTmp, 0, q)
		}
		if !isOrthonormal(q) {
			t.Errorf("Q is not orthonormal")
		}
		l := blas64.General{
			Rows:   m,
			Cols:   n,
			Stride: n,
			Data:   make([]float64, m*n),
		}
		if m >= n {
			for i := m - n; i < m; i++ {
				for j := 0; j <= min(i-(m-n), n-1); j++ {
					l.Data[i*l.Stride+j] = a[i*lda+j]
				}
			}
		} else {
			panic("untested")
		}
		ans := blas64.General{Rows: m, Cols: n, Stride: lda, Data: make([]float64, len(a))}
		copy(ans.Data, a)

		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, q, l, 0, ans)
		if !floats.EqualApprox(ans.Data, aCopy, 1e-10) {
			t.Errorf("Reconstruction mismatch: m = %v, n = %v", m, n)
		}
	}
}
示例#18
0
文件: general.go 项目: jacobxk/lapack
// constructQ constructs the Q matrix from the result of dgeqrf and dgeqr2
func constructQ(kind string, m, n int, a []float64, lda int, tau []float64) blas64.General {
	k := min(m, n)
	var sz int
	switch kind {
	case "QR":
		sz = m
	case "LQ":
		sz = n
	}

	q := blas64.General{
		Rows:   sz,
		Cols:   sz,
		Stride: sz,
		Data:   make([]float64, sz*sz),
	}
	for i := 0; i < sz; i++ {
		q.Data[i*sz+i] = 1
	}
	qCopy := blas64.General{
		Rows:   q.Rows,
		Cols:   q.Cols,
		Stride: q.Stride,
		Data:   make([]float64, len(q.Data)),
	}
	for i := 0; i < k; i++ {
		h := blas64.General{
			Rows:   sz,
			Cols:   sz,
			Stride: sz,
			Data:   make([]float64, sz*sz),
		}
		for j := 0; j < sz; j++ {
			h.Data[j*sz+j] = 1
		}
		vVec := blas64.Vector{
			Inc:  1,
			Data: make([]float64, sz),
		}
		for j := 0; j < i; j++ {
			vVec.Data[j] = 0
		}
		vVec.Data[i] = 1
		switch kind {
		case "QR":
			for j := i + 1; j < sz; j++ {
				vVec.Data[j] = a[lda*j+i]
			}
		case "LQ":
			for j := i + 1; j < sz; j++ {
				vVec.Data[j] = a[i*lda+j]
			}
		}
		blas64.Ger(-tau[i], vVec, vVec, h)
		copy(qCopy.Data, q.Data)
		// Mulitply q by the new h
		switch kind {
		case "QR":
			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, h, 0, q)
		case "LQ":
			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, h, qCopy, 0, q)
		}
	}
	return q
}
示例#19
0
func testDgehrd(t *testing.T, impl Dgehrder, n, ilo, ihi, extra int, optwork bool, rnd *rand.Rand) {
	a := randomGeneral(n, n, n+extra, rnd)
	aCopy := a
	aCopy.Data = make([]float64, len(a.Data))
	copy(aCopy.Data, a.Data)

	var tau []float64
	if n > 1 {
		tau = nanSlice(n - 1)
	}

	var work []float64
	if optwork {
		work = nanSlice(1)
		impl.Dgehrd(n, ilo, ihi, nil, a.Stride, nil, work, -1)
		work = nanSlice(int(work[0]))
	} else {
		work = nanSlice(max(1, n))
	}

	impl.Dgehrd(n, ilo, ihi, a.Data, a.Stride, tau, work, len(work))

	if n == 0 {
		// Just make sure there is no panic.
		return
	}

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

	// Check any invalid modifications of a.
	if !generalOutsideAllNaN(a) {
		t.Errorf("%v: out-of-range write to A\n%v", prefix, a.Data)
	}
	for i := ilo; i <= ihi; i++ {
		for j := 0; j < min(ilo, i); j++ {
			if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] {
				t.Errorf("%v: unexpected modification of A[%v,%v]", prefix, i, j)
			}
		}
	}
	for i := ihi + 1; i < n; i++ {
		for j := 0; j < i; j++ {
			if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] {
				t.Errorf("%v: unexpected modification of A[%v,%v]", prefix, i, j)
			}
		}
	}
	for i := 0; i <= ilo; i++ {
		for j := i; j < ilo+1; j++ {
			if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] {
				t.Errorf("%v: unexpected modification at A[%v,%v]", prefix, i, j)
			}
		}
		for j := ihi + 1; j < n; j++ {
			if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] {
				t.Errorf("%v: unexpected modification at A[%v,%v]", prefix, i, j)
			}
		}
	}
	for i := ihi + 1; i < n; i++ {
		for j := i; j < n; j++ {
			if a.Data[i*a.Stride+j] != aCopy.Data[i*aCopy.Stride+j] {
				t.Errorf("%v: unexpected modification at A[%v,%v]", prefix, i, j)
			}
		}
	}

	// Check that tau has been assigned properly.
	for i, v := range tau {
		if math.IsNaN(v) {
			t.Errorf("%v: unexpected NaN at tau[%v]", prefix, i)
		}
	}

	// Extract Q and check that it is orthogonal.
	q := blas64.General{
		Rows:   n,
		Cols:   n,
		Stride: n,
		Data:   make([]float64, n*n),
	}
	for i := 0; i < q.Rows; i++ {
		q.Data[i*q.Stride+i] = 1
	}
	qCopy := q
	qCopy.Data = make([]float64, len(q.Data))
	for j := ilo; j < ihi; j++ {
		h := blas64.General{
			Rows:   n,
			Cols:   n,
			Stride: n,
			Data:   make([]float64, n*n),
		}
		for i := 0; i < h.Rows; i++ {
			h.Data[i*h.Stride+i] = 1
		}
		v := blas64.Vector{
			Inc:  1,
			Data: make([]float64, n),
		}
		v.Data[j+1] = 1
		for i := j + 2; i < ihi+1; i++ {
			v.Data[i] = a.Data[i*a.Stride+j]
		}
		blas64.Ger(-tau[j], v, v, h)
		copy(qCopy.Data, q.Data)
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, h, 0, q)
	}
	if !isOrthonormal(q) {
		t.Errorf("%v: Q is not orthogonal\nQ=%v", prefix, q)
	}

	// Construct Q^T * AOrig * Q and check that it is upper Hessenberg.
	aq := blas64.General{
		Rows:   n,
		Cols:   n,
		Stride: n,
		Data:   make([]float64, n*n),
	}
	blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, aCopy, q, 0, aq)
	qaq := blas64.General{
		Rows:   n,
		Cols:   n,
		Stride: n,
		Data:   make([]float64, n*n),
	}
	blas64.Gemm(blas.Trans, blas.NoTrans, 1, q, aq, 0, qaq)
	for i := 0; i <= ilo; i++ {
		for j := ilo + 1; j <= ihi; j++ {
			qaqij := qaq.Data[i*qaq.Stride+j]
			diff := qaqij - a.Data[i*a.Stride+j]
			if math.Abs(diff) > 1e-13 {
				t.Errorf("%v: Q^T*AOrig*Q and A are not equal, diff at [%v,%v]=%v", prefix, i, j, diff)
			}
		}
	}
	for i := ilo + 1; i <= ihi; i++ {
		for j := ilo; j < n; j++ {
			qaqij := qaq.Data[i*qaq.Stride+j]
			if j < i-1 {
				if math.Abs(qaqij) > 1e-13 {
					t.Errorf("%v: Q^T*AOrig*Q is not upper Hessenberg, [%v,%v]=%v", prefix, i, j, qaqij)
				}
				continue
			}
			diff := qaqij - a.Data[i*a.Stride+j]
			if math.Abs(diff) > 1e-13 {
				t.Errorf("%v: Q^T*AOrig*Q and A are not equal, diff at [%v,%v]=%v", prefix, i, j, diff)
			}
		}
	}
}
示例#20
0
// constructQPBidiagonal constructs Q or P from the Bidiagonal decomposition
// computed by dlabrd and bgebd2.
func constructQPBidiagonal(vect lapack.DecompUpdate, m, n, nb int, a []float64, lda int, tau []float64) blas64.General {
	sz := n
	if vect == lapack.ApplyQ {
		sz = m
	}

	var ldv int
	var v blas64.General
	if vect == lapack.ApplyQ {
		ldv = nb
		v = blas64.General{
			Rows:   m,
			Cols:   nb,
			Stride: ldv,
			Data:   make([]float64, m*ldv),
		}
	} else {
		ldv = n
		v = blas64.General{
			Rows:   nb,
			Cols:   n,
			Stride: ldv,
			Data:   make([]float64, m*ldv),
		}
	}

	if vect == lapack.ApplyQ {
		if m >= n {
			for i := 0; i < m; i++ {
				for j := 0; j <= min(nb-1, i); j++ {
					if i == j {
						v.Data[i*ldv+j] = 1
						continue
					}
					v.Data[i*ldv+j] = a[i*lda+j]
				}
			}
		} else {
			for i := 1; i < m; i++ {
				for j := 0; j <= min(nb-1, i-1); j++ {
					if i-1 == j {
						v.Data[i*ldv+j] = 1
						continue
					}
					v.Data[i*ldv+j] = a[i*lda+j]
				}
			}
		}
	} else {
		if m < n {
			for i := 0; i < nb; i++ {
				for j := i; j < n; j++ {
					if i == j {
						v.Data[i*ldv+j] = 1
						continue
					}
					v.Data[i*ldv+j] = a[i*lda+j]
				}
			}
		} else {
			for i := 0; i < nb; i++ {
				for j := i + 1; j < n; j++ {
					if j-1 == i {
						v.Data[i*ldv+j] = 1
						continue
					}
					v.Data[i*ldv+j] = a[i*lda+j]
				}
			}
		}
	}

	// The variable name is a computation of Q, but the algorithm is mostly the
	// same for computing P (just with different data).
	qMat := blas64.General{
		Rows:   sz,
		Cols:   sz,
		Stride: sz,
		Data:   make([]float64, sz*sz),
	}
	hMat := blas64.General{
		Rows:   sz,
		Cols:   sz,
		Stride: sz,
		Data:   make([]float64, sz*sz),
	}
	// set Q to I
	for i := 0; i < sz; i++ {
		qMat.Data[i*qMat.Stride+i] = 1
	}
	for i := 0; i < nb; i++ {
		qCopy := blas64.General{Rows: qMat.Rows, Cols: qMat.Cols, Stride: qMat.Stride, Data: make([]float64, len(qMat.Data))}
		copy(qCopy.Data, qMat.Data)

		// Set g and h to I
		for i := 0; i < sz; i++ {
			for j := 0; j < sz; j++ {
				if i == j {
					hMat.Data[i*sz+j] = 1
				} else {
					hMat.Data[i*sz+j] = 0
				}
			}
		}
		var vi blas64.Vector
		// H -= tauQ[i] * v[i] * v[i]^t
		if vect == lapack.ApplyQ {
			vi = blas64.Vector{
				Inc:  v.Stride,
				Data: v.Data[i:],
			}
		} else {
			vi = blas64.Vector{
				Inc:  1,
				Data: v.Data[i*v.Stride:],
			}
		}
		blas64.Ger(-tau[i], vi, vi, hMat)
		// Q = Q * G[1]
		blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, hMat, 0, qMat)
	}
	return qMat
}
示例#21
0
func DlarfgTest(t *testing.T, impl Dlarfger) {
	for i, test := range []struct {
		alpha float64
		n     int
		x     []float64
	}{
		{
			alpha: 4,
			n:     3,
		},
		{
			alpha: -2,
			n:     3,
		},
		{
			alpha: 0,
			n:     3,
		},
		{
			alpha: 1,
			n:     1,
		},
		{
			alpha: 1,
			n:     2,
			x:     []float64{4, 5, 6},
		},
	} {
		n := test.n
		incX := 1
		var x []float64
		if test.x == nil {
			x = make([]float64, n-1)
			for i := range x {
				x[i] = rand.Float64()
			}
		} else {
			x = make([]float64, n-1)
			copy(x, test.x)
		}
		xcopy := make([]float64, n-1)
		copy(xcopy, x)
		alpha := test.alpha
		beta, tau := impl.Dlarfg(n, alpha, x, incX)

		// Verify the returns and the values in v. Construct h and perform
		// the explicit multiplication.
		h := make([]float64, n*n)
		for i := 0; i < n; i++ {
			h[i*n+i] = 1
		}
		hmat := blas64.General{
			Rows:   n,
			Cols:   n,
			Stride: n,
			Data:   h,
		}
		v := make([]float64, n)
		copy(v[1:], x)
		v[0] = 1
		vVec := blas64.Vector{
			Inc:  1,
			Data: v,
		}
		blas64.Ger(-tau, vVec, vVec, hmat)
		eye := blas64.General{
			Rows:   n,
			Cols:   n,
			Stride: n,
			Data:   make([]float64, n*n),
		}
		blas64.Gemm(blas.Trans, blas.NoTrans, 1, hmat, hmat, 0, eye)
		iseye := true
		for i := 0; i < n; i++ {
			for j := 0; j < n; j++ {
				if i == j {
					if math.Abs(eye.Data[i*n+j]-1) > 1e-14 {
						iseye = false
					}
				} else {
					if math.Abs(eye.Data[i*n+j]) > 1e-14 {
						iseye = false
					}
				}
			}
		}
		if !iseye {
			t.Errorf("H^T * H is not I %V", eye)
		}

		xVec := blas64.Vector{
			Inc:  1,
			Data: make([]float64, n),
		}
		xVec.Data[0] = test.alpha
		copy(xVec.Data[1:], xcopy)

		ans := make([]float64, n)
		ansVec := blas64.Vector{
			Inc:  1,
			Data: ans,
		}
		blas64.Gemv(blas.NoTrans, 1, hmat, xVec, 0, ansVec)
		if math.Abs(ans[0]-beta) > 1e-14 {
			t.Errorf("Case %v, beta mismatch. Want %v, got %v", i, ans[0], beta)
		}
		for i := 1; i < n; i++ {
			if math.Abs(ans[i]) > 1e-14 {
				t.Errorf("Case %v, nonzero answer %v", i, ans)
				break
			}
		}
	}
}
示例#22
0
func DlatrdTest(t *testing.T, impl Dlatrder) {
	rnd := rand.New(rand.NewSource(1))
	for _, uplo := range []blas.Uplo{blas.Upper, blas.Lower} {
		for _, test := range []struct {
			n, nb, lda, ldw int
		}{
			{5, 2, 0, 0},
			{5, 5, 0, 0},

			{5, 3, 10, 11},
			{5, 5, 10, 11},
		} {
			n := test.n
			nb := test.nb
			lda := test.lda
			if lda == 0 {
				lda = n
			}
			ldw := test.ldw
			if ldw == 0 {
				ldw = nb
			}

			a := make([]float64, n*lda)
			for i := range a {
				a[i] = rnd.NormFloat64()
			}

			e := make([]float64, n-1)
			for i := range e {
				e[i] = math.NaN()
			}
			tau := make([]float64, n-1)
			for i := range tau {
				tau[i] = math.NaN()
			}
			w := make([]float64, n*ldw)
			for i := range w {
				w[i] = math.NaN()
			}

			aCopy := make([]float64, len(a))
			copy(aCopy, a)

			impl.Dlatrd(uplo, n, nb, a, lda, e, tau, w, ldw)

			// Construct Q.
			ldq := n
			q := blas64.General{
				Rows:   n,
				Cols:   n,
				Stride: ldq,
				Data:   make([]float64, n*ldq),
			}
			for i := 0; i < n; i++ {
				q.Data[i*ldq+i] = 1
			}
			if uplo == blas.Upper {
				for i := n - 1; i >= n-nb; i-- {
					if i == 0 {
						continue
					}
					h := blas64.General{
						Rows: n, Cols: n, Stride: n, Data: make([]float64, n*n),
					}
					for j := 0; j < n; j++ {
						h.Data[j*n+j] = 1
					}
					v := blas64.Vector{
						Inc:  1,
						Data: make([]float64, n),
					}
					for j := 0; j < i-1; j++ {
						v.Data[j] = a[j*lda+i]
					}
					v.Data[i-1] = 1

					blas64.Ger(-tau[i-1], v, v, h)

					qTmp := blas64.General{
						Rows: n, Cols: n, Stride: n, Data: make([]float64, n*n),
					}
					copy(qTmp.Data, q.Data)
					blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qTmp, h, 0, q)
				}
			} else {
				for i := 0; i < nb; i++ {
					if i == n-1 {
						continue
					}
					h := blas64.General{
						Rows: n, Cols: n, Stride: n, Data: make([]float64, n*n),
					}
					for j := 0; j < n; j++ {
						h.Data[j*n+j] = 1
					}
					v := blas64.Vector{
						Inc:  1,
						Data: make([]float64, n),
					}
					v.Data[i+1] = 1
					for j := i + 2; j < n; j++ {
						v.Data[j] = a[j*lda+i]
					}
					blas64.Ger(-tau[i], v, v, h)

					qTmp := blas64.General{
						Rows: n, Cols: n, Stride: n, Data: make([]float64, n*n),
					}
					copy(qTmp.Data, q.Data)
					blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qTmp, h, 0, q)
				}
			}
			errStr := fmt.Sprintf("isUpper = %v, n = %v, nb = %v", uplo == blas.Upper, n, nb)
			if !isOrthonormal(q) {
				t.Errorf("Q not orthonormal. %s", errStr)
			}
			aGen := genFromSym(blas64.Symmetric{N: n, Stride: lda, Uplo: uplo, Data: aCopy})
			if !dlatrdCheckDecomposition(t, uplo, n, nb, e, tau, a, lda, aGen, q) {
				t.Errorf("Decomposition mismatch. %s", errStr)
			}
		}
	}
}
示例#23
0
func Dsytd2Test(t *testing.T, impl Dsytd2er) {
	rnd := rand.New(rand.NewSource(1))
	for _, uplo := range []blas.Uplo{blas.Upper, blas.Lower} {
		for _, test := range []struct {
			n, lda int
		}{
			{3, 0},
			{4, 0},
			{5, 0},

			{3, 10},
			{4, 10},
			{5, 10},
		} {
			n := test.n
			lda := test.lda
			if lda == 0 {
				lda = n
			}
			a := make([]float64, n*lda)
			for i := range a {
				a[i] = rnd.NormFloat64()
			}
			aCopy := make([]float64, len(a))
			copy(aCopy, a)

			d := make([]float64, n)
			for i := range d {
				d[i] = math.NaN()
			}
			e := make([]float64, n-1)
			for i := range e {
				e[i] = math.NaN()
			}
			tau := make([]float64, n-1)
			for i := range tau {
				tau[i] = math.NaN()
			}

			impl.Dsytd2(uplo, n, a, lda, d, e, tau)

			// Construct Q
			qMat := blas64.General{
				Rows:   n,
				Cols:   n,
				Stride: n,
				Data:   make([]float64, n*n),
			}
			qCopy := blas64.General{
				Rows:   n,
				Cols:   n,
				Stride: n,
				Data:   make([]float64, len(qMat.Data)),
			}
			// Set Q to I.
			for i := 0; i < n; i++ {
				qMat.Data[i*qMat.Stride+i] = 1
			}
			for i := 0; i < n-1; i++ {
				hMat := blas64.General{
					Rows:   n,
					Cols:   n,
					Stride: n,
					Data:   make([]float64, n*n),
				}
				// Set H to I.
				for i := 0; i < n; i++ {
					hMat.Data[i*hMat.Stride+i] = 1
				}
				var vi blas64.Vector
				if uplo == blas.Upper {
					vi = blas64.Vector{
						Inc:  1,
						Data: make([]float64, n),
					}
					for j := 0; j < i; j++ {
						vi.Data[j] = a[j*lda+i+1]
					}
					vi.Data[i] = 1
				} else {
					vi = blas64.Vector{
						Inc:  1,
						Data: make([]float64, n),
					}
					vi.Data[i+1] = 1
					for j := i + 2; j < n; j++ {
						vi.Data[j] = a[j*lda+i]
					}
				}
				blas64.Ger(-tau[i], vi, vi, hMat)
				copy(qCopy.Data, qMat.Data)

				// Multiply q by the new h.
				if uplo == blas.Upper {
					blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, hMat, qCopy, 0, qMat)
				} else {
					blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, qCopy, hMat, 0, qMat)
				}
			}
			// Check that Q is orthonormal
			othonormal := true
			for i := 0; i < n; i++ {
				for j := i; j < n; j++ {
					dot := blas64.Dot(n,
						blas64.Vector{Inc: 1, Data: qMat.Data[i*qMat.Stride:]},
						blas64.Vector{Inc: 1, Data: qMat.Data[j*qMat.Stride:]},
					)
					if i == j {
						if math.Abs(dot-1) > 1e-10 {
							othonormal = false
						}
					} else {
						if math.Abs(dot) > 1e-10 {
							othonormal = false
						}
					}
				}
			}
			if !othonormal {
				t.Errorf("Q not orthonormal")
			}

			// Compute Q^T * A * Q.
			aMat := blas64.General{
				Rows:   n,
				Cols:   n,
				Stride: n,
				Data:   make([]float64, len(a)),
			}

			for i := 0; i < n; i++ {
				for j := i; j < n; j++ {
					v := aCopy[i*lda+j]
					if uplo == blas.Lower {
						v = aCopy[j*lda+i]
					}
					aMat.Data[i*aMat.Stride+j] = v
					aMat.Data[j*aMat.Stride+i] = v
				}
			}

			tmp := blas64.General{
				Rows:   n,
				Cols:   n,
				Stride: n,
				Data:   make([]float64, n*n),
			}

			ans := blas64.General{
				Rows:   n,
				Cols:   n,
				Stride: n,
				Data:   make([]float64, n*n),
			}

			blas64.Gemm(blas.Trans, blas.NoTrans, 1, qMat, aMat, 0, tmp)
			blas64.Gemm(blas.NoTrans, blas.NoTrans, 1, tmp, qMat, 0, ans)

			// Compare with T.
			tMat := blas64.General{
				Rows:   n,
				Cols:   n,
				Stride: n,
				Data:   make([]float64, n*n),
			}
			for i := 0; i < n-1; i++ {
				tMat.Data[i*tMat.Stride+i] = d[i]
				tMat.Data[i*tMat.Stride+i+1] = e[i]
				tMat.Data[(i+1)*tMat.Stride+i] = e[i]
			}
			tMat.Data[(n-1)*tMat.Stride+n-1] = d[n-1]

			same := true
			for i := 0; i < n; i++ {
				for j := 0; j < n; j++ {
					if math.Abs(ans.Data[i*ans.Stride+j]-tMat.Data[i*tMat.Stride+j]) > 1e-10 {
						same = false
					}
				}
			}
			if !same {
				t.Errorf("Matrix answer mismatch")
			}
		}
	}
}