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 }
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) } } }
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 }
// 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) }
// 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 }
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) } } } }
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]) } }
// 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) } }
// 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) } }
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) }
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) } }
// 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 }
// 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 }
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) } } }
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) } } } }
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) } } }
// 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 }
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) } } } }
// 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 }
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 } } } }
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) } } } }
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") } } } }