matrix/mat64: provide sugar for easy matrix extraction

This commit is contained in:
kortschak
2017-05-27 08:05:00 +09:30
committed by Dan Kortschak
parent 15641d34f4
commit 67bc20e87d
14 changed files with 166 additions and 80 deletions

View File

@@ -205,7 +205,8 @@ func (gsvd *GSVD) ValuesB(s []float64) []float64 {
// ZeroRTo extracts the matrix [ 0 R ] from the singular value decomposition, storing // ZeroRTo extracts the matrix [ 0 R ] from the singular value decomposition, storing
// the result in-place into dst. [ 0 R ] is size (k+l)×c. // the result in-place into dst. [ 0 R ] is size (k+l)×c.
func (gsvd *GSVD) ZeroRTo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting ZeroR matrix is returned.
func (gsvd *GSVD) ZeroRTo(dst *Dense) *Dense {
if gsvd.kind == 0 { if gsvd.kind == 0 {
panic("gsvd: no decomposition computed") panic("gsvd: no decomposition computed")
} }
@@ -214,7 +215,11 @@ func (gsvd *GSVD) ZeroRTo(dst *Dense) {
k := gsvd.k k := gsvd.k
l := gsvd.l l := gsvd.l
h := min(k+l, r) h := min(k+l, r)
dst.reuseAsZeroed(k+l, c) if dst == nil {
dst = NewDense(k+l, c, nil)
} else {
dst.reuseAsZeroed(k+l, c)
}
a := Dense{ a := Dense{
mat: gsvd.a, mat: gsvd.a,
capRows: r, capRows: r,
@@ -231,29 +236,37 @@ func (gsvd *GSVD) ZeroRTo(dst *Dense) {
dst.Slice(r, k+l, c+r-k-l, c).(*Dense). dst.Slice(r, k+l, c+r-k-l, c).(*Dense).
Copy(b.Slice(r-k, l, c+r-k-l, c)) Copy(b.Slice(r-k, l, c+r-k-l, c))
} }
return dst
} }
// SigmaATo extracts the matrix Σ₁ from the singular value decomposition, storing // SigmaATo extracts the matrix Σ₁ from the singular value decomposition, storing
// the result in-place into dst. Σ₁ is size r×(k+l). // the result in-place into dst. Σ₁ is size r×(k+l).
func (gsvd *GSVD) SigmaATo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting SigmaA matrix is returned.
func (gsvd *GSVD) SigmaATo(dst *Dense) *Dense {
if gsvd.kind == 0 { if gsvd.kind == 0 {
panic("gsvd: no decomposition computed") panic("gsvd: no decomposition computed")
} }
r := gsvd.r r := gsvd.r
k := gsvd.k k := gsvd.k
l := gsvd.l l := gsvd.l
dst.reuseAsZeroed(r, k+l) if dst == nil {
dst = NewDense(r, k+l, nil)
} else {
dst.reuseAsZeroed(r, k+l)
}
for i := 0; i < k; i++ { for i := 0; i < k; i++ {
dst.set(i, i, 1) dst.set(i, i, 1)
} }
for i := k; i < min(r, k+l); i++ { for i := k; i < min(r, k+l); i++ {
dst.set(i, i, gsvd.s1[i]) dst.set(i, i, gsvd.s1[i])
} }
return dst
} }
// SigmaBTo extracts the matrix Σ₂ from the singular value decomposition, storing // SigmaBTo extracts the matrix Σ₂ from the singular value decomposition, storing
// the result in-place into dst. Σ₂ is size p×(k+l). // the result in-place into dst. Σ₂ is size p×(k+l).
func (gsvd *GSVD) SigmaBTo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting SigmaB matrix is returned.
func (gsvd *GSVD) SigmaBTo(dst *Dense) *Dense {
if gsvd.kind == 0 { if gsvd.kind == 0 {
panic("gsvd: no decomposition computed") panic("gsvd: no decomposition computed")
} }
@@ -261,24 +274,34 @@ func (gsvd *GSVD) SigmaBTo(dst *Dense) {
p := gsvd.p p := gsvd.p
k := gsvd.k k := gsvd.k
l := gsvd.l l := gsvd.l
dst.reuseAsZeroed(p, k+l) if dst == nil {
dst = NewDense(p, k+l, nil)
} else {
dst.reuseAsZeroed(p, k+l)
}
for i := 0; i < min(l, r-k); i++ { for i := 0; i < min(l, r-k); i++ {
dst.set(i, i+k, gsvd.s2[k+i]) dst.set(i, i+k, gsvd.s2[k+i])
} }
for i := r - k; i < l; i++ { for i := r - k; i < l; i++ {
dst.set(i, i+k, 1) dst.set(i, i+k, 1)
} }
return dst
} }
// UTo extracts the matrix U from the singular value decomposition, storing // UTo extracts the matrix U from the singular value decomposition, storing
// the result in-place into dst. U is size r×r. // the result in-place into dst. U is size r×r.
func (gsvd *GSVD) UTo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting U matrix is returned.
func (gsvd *GSVD) UTo(dst *Dense) *Dense {
if gsvd.kind&matrix.GSVDU == 0 { if gsvd.kind&matrix.GSVDU == 0 {
panic("mat64: improper GSVD kind") panic("mat64: improper GSVD kind")
} }
r := gsvd.u.Rows r := gsvd.u.Rows
c := gsvd.u.Cols c := gsvd.u.Cols
dst.reuseAs(r, c) if dst == nil {
dst = NewDense(r, c, nil)
} else {
dst.reuseAs(r, c)
}
tmp := &Dense{ tmp := &Dense{
mat: gsvd.u, mat: gsvd.u,
@@ -286,17 +309,23 @@ func (gsvd *GSVD) UTo(dst *Dense) {
capCols: c, capCols: c,
} }
dst.Copy(tmp) dst.Copy(tmp)
return dst
} }
// VTo extracts the matrix V from the singular value decomposition, storing // VTo extracts the matrix V from the singular value decomposition, storing
// the result in-place into dst. V is size p×p. // the result in-place into dst. V is size p×p.
func (gsvd *GSVD) VTo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting V matrix is returned.
func (gsvd *GSVD) VTo(dst *Dense) *Dense {
if gsvd.kind&matrix.GSVDV == 0 { if gsvd.kind&matrix.GSVDV == 0 {
panic("mat64: improper GSVD kind") panic("mat64: improper GSVD kind")
} }
r := gsvd.v.Rows r := gsvd.v.Rows
c := gsvd.v.Cols c := gsvd.v.Cols
dst.reuseAs(r, c) if dst == nil {
dst = NewDense(r, c, nil)
} else {
dst.reuseAs(r, c)
}
tmp := &Dense{ tmp := &Dense{
mat: gsvd.v, mat: gsvd.v,
@@ -304,17 +333,23 @@ func (gsvd *GSVD) VTo(dst *Dense) {
capCols: c, capCols: c,
} }
dst.Copy(tmp) dst.Copy(tmp)
return dst
} }
// QTo extracts the matrix Q from the singular value decomposition, storing // QTo extracts the matrix Q from the singular value decomposition, storing
// the result in-place into dst. Q is size c×c. // the result in-place into dst. Q is size c×c.
func (gsvd *GSVD) QTo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting Q matrix is returned.
func (gsvd *GSVD) QTo(dst *Dense) *Dense {
if gsvd.kind&matrix.GSVDQ == 0 { if gsvd.kind&matrix.GSVDQ == 0 {
panic("mat64: improper GSVD kind") panic("mat64: improper GSVD kind")
} }
r := gsvd.q.Rows r := gsvd.q.Rows
c := gsvd.q.Cols c := gsvd.q.Cols
dst.reuseAs(r, c) if dst == nil {
dst = NewDense(r, c, nil)
} else {
dst.reuseAs(r, c)
}
tmp := &Dense{ tmp := &Dense{
mat: gsvd.q, mat: gsvd.q,
@@ -322,4 +357,5 @@ func (gsvd *GSVD) QTo(dst *Dense) {
capCols: c, capCols: c,
} }
dst.Copy(tmp) dst.Copy(tmp)
return dst
} }

View File

@@ -25,22 +25,19 @@ func ExampleGSVD() {
log.Fatal("GSVD factorization failed") log.Fatal("GSVD factorization failed")
} }
var u, v mat64.Dense u := gsvd.UTo(nil)
gsvd.UTo(&u) v := gsvd.VTo(nil)
gsvd.VTo(&v)
s1 := gsvd.ValuesA(nil) s1 := gsvd.ValuesA(nil)
s2 := gsvd.ValuesB(nil) s2 := gsvd.ValuesB(nil)
fmt.Printf("Africa\n\ts1 = %.4f\n\n\tU = %.4f\n\n", fmt.Printf("Africa\n\ts1 = %.4f\n\n\tU = %.4f\n\n",
s1, mat64.Formatted(&u, mat64.Prefix("\t "), mat64.Excerpt(2))) s1, mat64.Formatted(u, mat64.Prefix("\t "), mat64.Excerpt(2)))
fmt.Printf("Latin America/Caribbean\n\ts2 = %.4f\n\n\tV = %.4f\n", fmt.Printf("Latin America/Caribbean\n\ts2 = %.4f\n\n\tV = %.4f\n",
s2, mat64.Formatted(&v, mat64.Prefix("\t "), mat64.Excerpt(2))) s2, mat64.Formatted(v, mat64.Prefix("\t "), mat64.Excerpt(2)))
var zeroR, q mat64.Dense var q mat64.Dense
gsvd.ZeroRTo(&zeroR) q.Mul(gsvd.ZeroRTo(nil), gsvd.QTo(nil))
gsvd.QTo(&q)
q.Mul(&zeroR, &q)
fmt.Printf("\nCommon basis vectors\n\n\tQ^T = %.4f\n", fmt.Printf("\nCommon basis vectors\n\n\tQ^T = %.4f\n",
mat64.Formatted(q.T(), mat64.Prefix("\t "))) mat64.Formatted(q.T(), mat64.Prefix("\t ")))

View File

@@ -106,14 +106,13 @@ func TestGSVD(t *testing.T) {
} }
func extractGSVD(gsvd *GSVD) (c, s []float64, s1, s2, zR, u, v, q *Dense) { func extractGSVD(gsvd *GSVD) (c, s []float64, s1, s2, zR, u, v, q *Dense) {
var s1m, s2m, zeroR, um, vm, qm Dense s1 = gsvd.SigmaATo(nil)
gsvd.SigmaATo(&s1m) s2 = gsvd.SigmaBTo(nil)
gsvd.SigmaBTo(&s2m) zR = gsvd.ZeroRTo(nil)
gsvd.ZeroRTo(&zeroR) u = gsvd.UTo(nil)
gsvd.UTo(&um) v = gsvd.VTo(nil)
gsvd.VTo(&vm) q = gsvd.QTo(nil)
gsvd.QTo(&qm)
c = gsvd.ValuesA(nil) c = gsvd.ValuesA(nil)
s = gsvd.ValuesB(nil) s = gsvd.ValuesB(nil)
return c, s, &s1m, &s2m, &zeroR, &um, &vm, &qm return c, s, s1, s2, zR, u, v, q
} }

View File

@@ -143,9 +143,10 @@ func (gsvd *HOGSVD) Len() int {
// UTo extracts the matrix U_n from the singular value decomposition, storing // UTo extracts the matrix U_n from the singular value decomposition, storing
// the result in-place into dst. U_n is size r×c. // the result in-place into dst. U_n is size r×c.
// If dst is nil, a new matrix is allocated. The resulting U matrix is returned.
// //
// UTo will panic if the receiver does not contain a successful factorization. // UTo will panic if the receiver does not contain a successful factorization.
func (gsvd *HOGSVD) UTo(dst *Dense, n int) { func (gsvd *HOGSVD) UTo(dst *Dense, n int) *Dense {
if gsvd.n == 0 { if gsvd.n == 0 {
panic("hogsvd: unsuccessful factorization") panic("hogsvd: unsuccessful factorization")
} }
@@ -153,12 +154,18 @@ func (gsvd *HOGSVD) UTo(dst *Dense, n int) {
panic("hogsvd: invalid index") panic("hogsvd: invalid index")
} }
dst.reuseAs(gsvd.b[n].Dims()) if dst == nil {
r, c := gsvd.b[n].Dims()
dst = NewDense(r, c, nil)
} else {
dst.reuseAs(gsvd.b[n].Dims())
}
dst.Copy(&gsvd.b[n]) dst.Copy(&gsvd.b[n])
for j, f := range gsvd.Values(nil, n) { for j, f := range gsvd.Values(nil, n) {
v := dst.ColView(j) v := dst.ColView(j)
v.ScaleVec(1/f, v) v.ScaleVec(1/f, v)
} }
return dst
} }
// Values returns the nth set of singular values of the factorized system. // Values returns the nth set of singular values of the factorized system.
@@ -190,12 +197,19 @@ func (gsvd *HOGSVD) Values(s []float64, n int) []float64 {
// VTo extracts the matrix V from the singular value decomposition, storing // VTo extracts the matrix V from the singular value decomposition, storing
// the result in-place into dst. V is size c×c. // the result in-place into dst. V is size c×c.
// If dst is nil, a new matrix is allocated. The resulting V matrix is returned.
// //
// VTo will panic if the receiver does not contain a successful factorization. // VTo will panic if the receiver does not contain a successful factorization.
func (gsvd *HOGSVD) VTo(dst *Dense) { func (gsvd *HOGSVD) VTo(dst *Dense) *Dense {
if gsvd.n == 0 { if gsvd.n == 0 {
panic("hogsvd: unsuccessful factorization") panic("hogsvd: unsuccessful factorization")
} }
dst.reuseAs(gsvd.v.Dims()) if dst == nil {
r, c := gsvd.v.Dims()
dst = NewDense(r, c, nil)
} else {
dst.reuseAs(gsvd.v.Dims())
}
dst.Copy(gsvd.v) dst.Copy(gsvd.v)
return dst
} }

View File

@@ -24,15 +24,13 @@ func ExampleHOGSVD() {
} }
for i, n := range []string{"Africa", "Asia", "Latin America/Caribbean", "Oceania"} { for i, n := range []string{"Africa", "Asia", "Latin America/Caribbean", "Oceania"} {
var u mat64.Dense u := gsvd.UTo(nil, i)
gsvd.UTo(&u, i)
s := gsvd.Values(nil, i) s := gsvd.Values(nil, i)
fmt.Printf("%s\n\ts_%d = %.4f\n\n\tU_%[2]d = %.4[4]f\n", fmt.Printf("%s\n\ts_%d = %.4f\n\n\tU_%[2]d = %.4[4]f\n",
n, i, s, mat64.Formatted(&u, mat64.Prefix("\t "))) n, i, s, mat64.Formatted(u, mat64.Prefix("\t ")))
} }
var v mat64.Dense v := gsvd.VTo(nil)
gsvd.VTo(&v)
fmt.Printf("\nCommon basis vectors\n\n\tV^T = %.4f", fmt.Printf("\nCommon basis vectors\n\n\tV^T = %.4f",
mat64.Formatted(v.T(), mat64.Prefix("\t "))) mat64.Formatted(v.T(), mat64.Prefix("\t ")))

View File

@@ -79,11 +79,9 @@ func extractHOGSVD(gsvd *HOGSVD) (u []*Dense, s [][]float64, v *Dense) {
u = make([]*Dense, gsvd.Len()) u = make([]*Dense, gsvd.Len())
s = make([][]float64, gsvd.Len()) s = make([][]float64, gsvd.Len())
for i := 0; i < gsvd.Len(); i++ { for i := 0; i < gsvd.Len(); i++ {
u[i] = &Dense{} u[i] = gsvd.UTo(nil, i)
gsvd.UTo(u[i], i)
s[i] = gsvd.Values(nil, i) s[i] = gsvd.Values(nil, i)
} }
v = &Dense{} v = gsvd.VTo(nil)
gsvd.VTo(v)
return u, s, v return u, s, v
} }

View File

@@ -59,11 +59,16 @@ func (lq *LQ) Factorize(a Matrix) {
// and upper triangular matrices. // and upper triangular matrices.
// LTo extracts the m×n lower trapezoidal matrix from a LQ decomposition. // LTo extracts the m×n lower trapezoidal matrix from a LQ decomposition.
func (lq *LQ) LTo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting L matrix is returned.
func (lq *LQ) LTo(dst *Dense) *Dense {
r, c := lq.lq.Dims() r, c := lq.lq.Dims()
dst.reuseAs(r, c) if dst == nil {
dst = NewDense(r, c, nil)
} else {
dst.reuseAs(r, c)
}
// Disguise the LQ as a lower triangular // Disguise the LQ as a lower triangular.
t := &TriDense{ t := &TriDense{
mat: blas64.Triangular{ mat: blas64.Triangular{
N: r, N: r,
@@ -77,18 +82,25 @@ func (lq *LQ) LTo(dst *Dense) {
dst.Copy(t) dst.Copy(t)
if r == c { if r == c {
return return dst
} }
// Zero right of the triangular. // Zero right of the triangular.
for i := 0; i < r; i++ { for i := 0; i < r; i++ {
zero(dst.mat.Data[i*dst.mat.Stride+r : i*dst.mat.Stride+c]) zero(dst.mat.Data[i*dst.mat.Stride+r : i*dst.mat.Stride+c])
} }
return dst
} }
// QTo extracts the n×n orthonormal matrix Q from an LQ decomposition. // QTo extracts the n×n orthonormal matrix Q from an LQ decomposition.
func (lq *LQ) QTo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting Q matrix is returned.
func (lq *LQ) QTo(dst *Dense) *Dense {
r, c := lq.lq.Dims() r, c := lq.lq.Dims()
dst.reuseAs(c, c) if dst == nil {
dst = NewDense(c, c, nil)
} else {
dst.reuseAs(c, c)
}
// Set Q = I. // Set Q = I.
for i := 0; i < c; i++ { for i := 0; i < c; i++ {
@@ -132,6 +144,8 @@ func (lq *LQ) QTo(dst *Dense) {
1, h, qCopy.mat, 1, h, qCopy.mat,
0, dst.mat) 0, dst.mat)
} }
return dst
} }
// SolveLQ finds a minimum-norm solution to a system of linear equations defined // SolveLQ finds a minimum-norm solution to a system of linear equations defined

View File

@@ -29,17 +29,16 @@ func TestLQ(t *testing.T) {
var lq LQ var lq LQ
lq.Factorize(a) lq.Factorize(a)
var l, q Dense q := lq.QTo(nil)
lq.QTo(&q)
if !isOrthonormal(&q, 1e-10) { if !isOrthonormal(q, 1e-10) {
t.Errorf("Q is not orthonormal: m = %v, n = %v", m, n) t.Errorf("Q is not orthonormal: m = %v, n = %v", m, n)
} }
lq.LTo(&l) l := lq.LTo(nil)
var got Dense var got Dense
got.Mul(&l, &q) got.Mul(l, q)
if !EqualApprox(&got, &want, 1e-12) { if !EqualApprox(&got, &want, 1e-12) {
t.Errorf("LQ does not equal original matrix. \nWant: %v\nGot: %v", want, got) t.Errorf("LQ does not equal original matrix. \nWant: %v\nGot: %v", want, got)
} }

View File

@@ -205,9 +205,14 @@ func (lu *LU) RankOne(orig *LU, alpha float64, x, y *Vector) {
} }
// LTo extracts the lower triangular matrix from an LU factorization. // LTo extracts the lower triangular matrix from an LU factorization.
func (lu *LU) LTo(dst *TriDense) { // If dst is nil, a new matrix is allocated. The resulting L matrix is returned.
func (lu *LU) LTo(dst *TriDense) *TriDense {
_, n := lu.lu.Dims() _, n := lu.lu.Dims()
dst.reuseAs(n, false) if dst == nil {
dst = NewTriDense(n, matrix.Lower, nil)
} else {
dst.reuseAs(n, matrix.Lower)
}
// Extract the lower triangular elements. // Extract the lower triangular elements.
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
for j := 0; j < i; j++ { for j := 0; j < i; j++ {
@@ -218,18 +223,25 @@ func (lu *LU) LTo(dst *TriDense) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
dst.mat.Data[i*dst.mat.Stride+i] = 1 dst.mat.Data[i*dst.mat.Stride+i] = 1
} }
return dst
} }
// UTo extracts the upper triangular matrix from an LU factorization. // UTo extracts the upper triangular matrix from an LU factorization.
func (lu *LU) UTo(dst *TriDense) { // If dst is nil, a new matrix is allocated. The resulting U matrix is returned.
func (lu *LU) UTo(dst *TriDense) *TriDense {
_, n := lu.lu.Dims() _, n := lu.lu.Dims()
dst.reuseAs(n, true) if dst == nil {
dst = NewTriDense(n, matrix.Upper, nil)
} else {
dst.reuseAs(n, matrix.Upper)
}
// Extract the upper triangular elements. // Extract the upper triangular elements.
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
for j := i; j < n; j++ { for j := i; j < n; j++ {
dst.mat.Data[i*dst.mat.Stride+j] = lu.lu.mat.Data[i*lu.lu.mat.Stride+j] dst.mat.Data[i*dst.mat.Stride+j] = lu.lu.mat.Data[i*lu.lu.mat.Stride+j]
} }
} }
return dst
} }
// Permutation constructs an r×r permutation matrix with the given row swaps. // Permutation constructs an r×r permutation matrix with the given row swaps.

View File

@@ -23,15 +23,13 @@ func TestLUD(t *testing.T) {
var lu LU var lu LU
lu.Factorize(a) lu.Factorize(a)
var l, u TriDense l := lu.LTo(nil)
lu.LTo(&l) u := lu.UTo(nil)
lu.UTo(&u)
var p Dense var p Dense
pivot := lu.Pivot(nil) pivot := lu.Pivot(nil)
p.Permutation(n, pivot) p.Permutation(n, pivot)
var got Dense var got Dense
got.Mul(&p, &l) got.Product(&p, l, u)
got.Mul(&got, &u)
if !EqualApprox(&got, &want, 1e-12) { if !EqualApprox(&got, &want, 1e-12) {
t.Errorf("PLU does not equal original matrix.\nWant: %v\n Got: %v", want, got) t.Errorf("PLU does not equal original matrix.\nWant: %v\n Got: %v", want, got)
} }

View File

@@ -61,9 +61,14 @@ func (qr *QR) Factorize(a Matrix) {
// and upper triangular matrices. // and upper triangular matrices.
// RTo extracts the m×n upper trapezoidal matrix from a QR decomposition. // RTo extracts the m×n upper trapezoidal matrix from a QR decomposition.
func (qr *QR) RTo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting dst matrix is returned.
func (qr *QR) RTo(dst *Dense) *Dense {
r, c := qr.qr.Dims() r, c := qr.qr.Dims()
dst.reuseAs(r, c) if dst == nil {
dst = NewDense(r, c, nil)
} else {
dst.reuseAs(r, c)
}
// Disguise the QR as an upper triangular // Disguise the QR as an upper triangular
t := &TriDense{ t := &TriDense{
@@ -82,12 +87,19 @@ func (qr *QR) RTo(dst *Dense) {
for i := r; i < c; i++ { for i := r; i < c; i++ {
zero(dst.mat.Data[i*dst.mat.Stride : i*dst.mat.Stride+c]) zero(dst.mat.Data[i*dst.mat.Stride : i*dst.mat.Stride+c])
} }
return dst
} }
// QTo extracts the m×m orthonormal matrix Q from a QR decomposition. // QTo extracts the m×m orthonormal matrix Q from a QR decomposition.
func (qr *QR) QTo(dst *Dense) { // If dst is nil, a new matrix is allocated. The resulting Q matrix is returned.
func (qr *QR) QTo(dst *Dense) *Dense {
r, _ := qr.qr.Dims() r, _ := qr.qr.Dims()
dst.reuseAsZeroed(r, r) if dst == nil {
dst = NewDense(r, r, nil)
} else {
dst.reuseAsZeroed(r, r)
}
// Set Q = I. // Set Q = I.
for i := 0; i < r*r; i += r + 1 { for i := 0; i < r*r; i += r + 1 {
@@ -99,6 +111,8 @@ func (qr *QR) QTo(dst *Dense) {
lapack64.Ormqr(blas.Left, blas.NoTrans, qr.qr.mat, qr.tau, dst.mat, work, -1) lapack64.Ormqr(blas.Left, blas.NoTrans, qr.qr.mat, qr.tau, dst.mat, work, -1)
work = make([]float64, int(work[0])) work = make([]float64, int(work[0]))
lapack64.Ormqr(blas.Left, blas.NoTrans, qr.qr.mat, qr.tau, dst.mat, work, len(work)) lapack64.Ormqr(blas.Left, blas.NoTrans, qr.qr.mat, qr.tau, dst.mat, work, len(work))
return dst
} }
// SolveQR finds a minimum-norm solution to a system of linear equations defined // SolveQR finds a minimum-norm solution to a system of linear equations defined

View File

@@ -32,17 +32,16 @@ func TestQR(t *testing.T) {
var qr QR var qr QR
qr.Factorize(a) qr.Factorize(a)
var q, r Dense q := qr.QTo(nil)
qr.QTo(&q)
if !isOrthonormal(&q, 1e-10) { if !isOrthonormal(q, 1e-10) {
t.Errorf("Q is not orthonormal: m = %v, n = %v", m, n) t.Errorf("Q is not orthonormal: m = %v, n = %v", m, n)
} }
qr.RTo(&r) r := qr.RTo(nil)
var got Dense var got Dense
got.Mul(&q, &r) got.Mul(q, r)
if !EqualApprox(&got, &want, 1e-12) { if !EqualApprox(&got, &want, 1e-12) {
t.Errorf("QR does not equal original matrix. \nWant: %v\nGot: %v", want, got) t.Errorf("QR does not equal original matrix. \nWant: %v\nGot: %v", want, got)
} }

View File

@@ -141,14 +141,18 @@ func (svd *SVD) Values(s []float64) []float64 {
// UTo extracts the matrix U from the singular value decomposition, storing // UTo extracts the matrix U from the singular value decomposition, storing
// the result in-place into dst. U is size m×m if svd.Kind() == SVDFull, // the result in-place into dst. U is size m×m if svd.Kind() == SVDFull,
// of size m×min(m,n) if svd.Kind() == SVDThin, and UTo panics otherwise. // of size m×min(m,n) if svd.Kind() == SVDThin, and UTo panics otherwise.
func (svd *SVD) UTo(dst *Dense) { func (svd *SVD) UTo(dst *Dense) *Dense {
kind := svd.kind kind := svd.kind
if kind != matrix.SVDFull && kind != matrix.SVDThin { if kind != matrix.SVDFull && kind != matrix.SVDThin {
panic("mat64: improper SVD kind") panic("mat64: improper SVD kind")
} }
r := svd.u.Rows r := svd.u.Rows
c := svd.u.Cols c := svd.u.Cols
dst.reuseAs(r, c) if dst == nil {
dst = NewDense(r, c, nil)
} else {
dst.reuseAs(r, c)
}
tmp := &Dense{ tmp := &Dense{
mat: svd.u, mat: svd.u,
@@ -156,19 +160,25 @@ func (svd *SVD) UTo(dst *Dense) {
capCols: c, capCols: c,
} }
dst.Copy(tmp) dst.Copy(tmp)
return dst
} }
// VTo extracts the matrix V from the singular value decomposition, storing // VTo extracts the matrix V from the singular value decomposition, storing
// the result in-place into dst. V is size n×n if svd.Kind() == SVDFull, // the result in-place into dst. V is size n×n if svd.Kind() == SVDFull,
// of size n×min(m,n) if svd.Kind() == SVDThin, and VTo panics otherwise. // of size n×min(m,n) if svd.Kind() == SVDThin, and VTo panics otherwise.
func (svd *SVD) VTo(dst *Dense) { func (svd *SVD) VTo(dst *Dense) *Dense {
kind := svd.kind kind := svd.kind
if kind != matrix.SVDFull && kind != matrix.SVDThin { if kind != matrix.SVDFull && kind != matrix.SVDThin {
panic("mat64: improper SVD kind") panic("mat64: improper SVD kind")
} }
r := svd.vt.Rows r := svd.vt.Rows
c := svd.vt.Cols c := svd.vt.Cols
dst.reuseAs(c, r) if dst == nil {
dst = NewDense(c, r, nil)
} else {
dst.reuseAs(c, r)
}
tmp := &Dense{ tmp := &Dense{
mat: svd.vt, mat: svd.vt,
@@ -176,4 +186,6 @@ func (svd *SVD) VTo(dst *Dense) {
capCols: c, capCols: c,
} }
dst.Copy(tmp.T()) dst.Copy(tmp.T())
return dst
} }

View File

@@ -169,9 +169,5 @@ func TestSVD(t *testing.T) {
} }
func extractSVD(svd *SVD) (s []float64, u, v *Dense) { func extractSVD(svd *SVD) (s []float64, u, v *Dense) {
var um, vm Dense return svd.Values(nil), svd.UTo(nil), svd.VTo(nil)
svd.UTo(&um)
svd.VTo(&vm)
s = svd.Values(nil)
return s, &um, &vm
} }