mirror of
				https://github.com/gonum/gonum.git
				synced 2025-10-31 18:42:45 +08:00 
			
		
		
		
	matrix/mat64: provide sugar for easy matrix extraction
This commit is contained in:
		| @@ -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) | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(k+l, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAsZeroed(k+l, c) | 		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 | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(r, k+l, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAsZeroed(r, k+l) | 		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 | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(p, k+l, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAsZeroed(p, k+l) | 		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 | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(r, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(r, c) | 		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 | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(r, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(r, c) | 		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 | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(r, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(r, c) | 		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 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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      "))) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if dst == nil { | ||||||
|  | 		r, c := gsvd.b[n].Dims() | ||||||
|  | 		dst = NewDense(r, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(gsvd.b[n].Dims()) | 		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") | ||||||
| 	} | 	} | ||||||
|  | 	if dst == nil { | ||||||
|  | 		r, c := gsvd.v.Dims() | ||||||
|  | 		dst = NewDense(r, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(gsvd.v.Dims()) | 		dst.reuseAs(gsvd.v.Dims()) | ||||||
| 	dst.Copy(gsvd.v) | 	} | ||||||
|  | 	dst.Copy(gsvd.v) | ||||||
|  | 	return dst | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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      "))) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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() | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(r, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(r, c) | 		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() | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(c, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(c, c) | 		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 | ||||||
|   | |||||||
| @@ -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) | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -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. | ||||||
|   | |||||||
| @@ -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) | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -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() | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(r, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(r, c) | 		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() | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(r, r, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAsZeroed(r, r) | 		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 | ||||||
|   | |||||||
| @@ -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) | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -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 | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(r, c, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(r, c) | 		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 | ||||||
|  | 	if dst == nil { | ||||||
|  | 		dst = NewDense(c, r, nil) | ||||||
|  | 	} else { | ||||||
| 		dst.reuseAs(c, r) | 		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 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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 |  | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 kortschak
					kortschak