From b10f3a00f3eca0aa851a579694de4b469d83b420 Mon Sep 17 00:00:00 2001 From: kortschak Date: Wed, 8 Jan 2014 09:56:39 +1030 Subject: [PATCH] Change NewDense signature and behaviour This is an API breaking change. NewDense now panics if len(mat) != r*c, unless mat == nil. When mat is nil a new, correctly sized slice is allocated. --- mat64/cholesky.go | 4 +-- mat64/cholesky_test.go | 4 +-- mat64/dense.go | 11 +++--- mat64/eigen.go | 4 +-- mat64/eigen_test.go | 28 ++++++++-------- mat64/lu.go | 4 +-- mat64/lu_test.go | 24 ++++++------- mat64/matrix.go | 2 +- mat64/matrix_test.go | 76 +++++++++++++----------------------------- mat64/qr.go | 6 ++-- mat64/qr_test.go | 2 +- mat64/svd.go | 6 ++-- mat64/svd_test.go | 28 ++++++++-------- 13 files changed, 87 insertions(+), 112 deletions(-) diff --git a/mat64/cholesky.go b/mat64/cholesky.go index 895b3640..65698cff 100644 --- a/mat64/cholesky.go +++ b/mat64/cholesky.go @@ -21,7 +21,7 @@ func Cholesky(a *Dense) CholeskyFactor { // Initialize. m, n := a.Dims() spd := m == n - l, _ := NewDense(n, n, make([]float64, n*n)) + l := NewDense(n, n, nil) // Main loop. lRowj := make([]float64, n) @@ -59,7 +59,7 @@ func CholeskyR(a *Dense) (r *Dense, spd bool) { // Initialize. m, n := a.Dims() spd = m == n - r, _ = NewDense(n, n, make([]float64, n*n)) + r = NewDense(n, n, nil) // Main loop. for j := 0; j < n; j++ { diff --git a/mat64/cholesky_test.go b/mat64/cholesky_test.go index be6f3f2c..6eb880a1 100644 --- a/mat64/cholesky_test.go +++ b/mat64/cholesky_test.go @@ -14,11 +14,11 @@ func (s *S) TestCholesky(c *check.C) { spd bool }{ { - a: mustDense(NewDense(3, 3, []float64{ + a: NewDense(3, 3, []float64{ 4, 1, 1, 1, 2, 3, 1, 3, 6, - })), + }), spd: true, }, diff --git a/mat64/dense.go b/mat64/dense.go index d0d7621a..71714bc6 100644 --- a/mat64/dense.go +++ b/mat64/dense.go @@ -62,9 +62,12 @@ type Dense struct { mat BlasMatrix } -func NewDense(r, c int, mat []float64) (*Dense, error) { - if r*c != len(mat) { - return nil, ErrShape +func NewDense(r, c int, mat []float64) *Dense { + if mat != nil && r*c != len(mat) { + panic(ErrShape) + } + if mat == nil { + mat = make([]float64, r*c) } return &Dense{BlasMatrix{ Order: BlasOrder, @@ -72,7 +75,7 @@ func NewDense(r, c int, mat []float64) (*Dense, error) { Cols: c, Stride: c, Data: mat, - }}, nil + }} } // DenseCopyOf returns a newly allocated copy of the elements of a. diff --git a/mat64/eigen.go b/mat64/eigen.go index 341a4d26..fb21e217 100644 --- a/mat64/eigen.go +++ b/mat64/eigen.go @@ -359,7 +359,7 @@ func orthes(a *Dense) (hess, v *Dense) { } // Accumulate transformations (Algol's ortran). - v, _ = NewDense(n, n, make([]float64, n*n)) + v = NewDense(n, n, nil) for i := 0; i < n; i++ { for j := 0; j < n; j++ { if i == j { @@ -811,7 +811,7 @@ func (f EigenFactors) D() *Dense { if n = len(d); n != len(e) { panic(ErrSquare) } - dm, _ := NewDense(n, n, make([]float64, n*n)) + dm := NewDense(n, n, nil) for i := 0; i < n; i++ { dm.Set(i, i, d[i]) if e[i] > 0 { diff --git a/mat64/eigen_test.go b/mat64/eigen_test.go index e9bc8137..6d34d622 100644 --- a/mat64/eigen_test.go +++ b/mat64/eigen_test.go @@ -19,66 +19,66 @@ func (s *S) TestEigen(c *check.C) { v *Dense }{ { - a: mustDense(NewDense(3, 3, []float64{ + a: NewDense(3, 3, []float64{ 1, 2, 1, 6, -1, 0, -1, -2, -1, - })), + }), epsilon: math.Pow(2, -52.0), d: []float64{3.0000000000000044, -4.000000000000003, -1.0980273383714707e-16}, e: []float64{0, 0, 0}, - v: mustDense(NewDense(3, 3, []float64{ + v: NewDense(3, 3, []float64{ -0.48507125007266627, 0.41649656391752204, 0.11785113019775795, -0.7276068751089995, -0.8329931278350428, 0.7071067811865481, 0.48507125007266627, -0.4164965639175216, -1.5320646925708532, - })), + }), }, { - a: mustDense(NewDense(3, 3, []float64{ + a: NewDense(3, 3, []float64{ 1, 6, -1, 6, -1, -2, -1, -2, -1, - })), + }), epsilon: math.Pow(2, -52.0), d: []float64{-6.240753470718579, -1.3995889142010132, 6.640342384919599}, e: []float64{0, 0, 0}, - v: mustDense(NewDense(3, 3, []float64{ + v: NewDense(3, 3, []float64{ -0.6134279348516111, -0.31411097261113, -0.7245967607083111, 0.7697297716508223, -0.03251534945303795, -0.6375412384185983, 0.17669818159240022, -0.9488293044247931, 0.2617263908869383, - })), + }), }, { // Jama pvals - a: mustDense(NewDense(3, 3, []float64{ + a: NewDense(3, 3, []float64{ 4, 1, 1, 1, 2, 3, 1, 3, 6, - })), + }), epsilon: math.Pow(2, -52.0), }, { // Jama evals - a: mustDense(NewDense(4, 4, []float64{ + a: NewDense(4, 4, []float64{ 0, 1, 0, 0, 1, 0, 2e-7, 0, 0, -2e-7, 0, 1, 0, 0, 1, 0, - })), + }), epsilon: math.Pow(2, -52.0), }, { // Jama badeigs - a: mustDense(NewDense(5, 5, []float64{ + a: NewDense(5, 5, []float64{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, - })), + }), epsilon: math.Pow(2, -52.0), }, diff --git a/mat64/lu.go b/mat64/lu.go index e2ec6f9f..e50212bf 100644 --- a/mat64/lu.go +++ b/mat64/lu.go @@ -173,7 +173,7 @@ func (f LUFactors) IsSingular() bool { func (f LUFactors) L() *Dense { lu := f.LU m, n := lu.Dims() - l, _ := NewDense(m, n, make([]float64, m*n)) + l := NewDense(m, n, nil) for i := 0; i < m; i++ { for j := 0; j < n; j++ { if i > j { @@ -190,7 +190,7 @@ func (f LUFactors) L() *Dense { func (f LUFactors) U() *Dense { lu := f.LU m, n := lu.Dims() - u, _ := NewDense(m, n, make([]float64, m*n)) + u := NewDense(m, n, nil) for i := 0; i < n; i++ { for j := 0; j < n; j++ { if i <= j { diff --git a/mat64/lu_test.go b/mat64/lu_test.go index aac1e62f..c0d376cc 100644 --- a/mat64/lu_test.go +++ b/mat64/lu_test.go @@ -19,22 +19,22 @@ func (s *S) TestLUD(c *check.C) { sign int }{ { // This is a hard coded equivalent of the approach used in the Jama LU test. - a: mustDense(NewDense(3, 3, []float64{ + a: NewDense(3, 3, []float64{ 0, 2, 3, 4, 5, 6, 7, 8, 9, - })), + }), - l: mustDense(NewDense(3, 3, []float64{ + l: NewDense(3, 3, []float64{ 1, 0, 0, 0, 1, 0, 0.5714285714285714, 0.2142857142857144, 1, - })), - u: mustDense(NewDense(3, 3, []float64{ + }), + u: NewDense(3, 3, []float64{ 7, 8, 9, 0, 2, 3, 0, 0, 0.2142857142857144, - })), + }), pivot: []int{ 2, // 0 0 1 0, // 1 0 0 @@ -78,22 +78,22 @@ func (s *S) TestLUDGaussian(c *check.C) { sign int }{ { // This is a hard coded equivalent of the approach used in the Jama LU test. - a: mustDense(NewDense(3, 3, []float64{ + a: NewDense(3, 3, []float64{ 0, 2, 3, 4, 5, 6, 7, 8, 9, - })), + }), - l: mustDense(NewDense(3, 3, []float64{ + l: NewDense(3, 3, []float64{ 1, 0, 0, 0, 1, 0, 0.5714285714285714, 0.2142857142857144, 1, - })), - u: mustDense(NewDense(3, 3, []float64{ + }), + u: NewDense(3, 3, []float64{ 7, 8, 9, 0, 2, 3, 0, 0, 0.2142857142857144, - })), + }), pivot: []int{ 2, // 0 0 1 0, // 1 0 0 diff --git a/mat64/matrix.go b/mat64/matrix.go index 2e6b5df2..29324aa3 100644 --- a/mat64/matrix.go +++ b/mat64/matrix.go @@ -289,7 +289,7 @@ func Inverse(a Matrix) *Dense { for i := 0; i < m*m; i += m + 1 { d[i] = 1 } - eye, _ := NewDense(m, m, d) + eye := NewDense(m, m, d) return Solve(a, eye) } diff --git a/mat64/matrix_test.go b/mat64/matrix_test.go index b4d0a15d..26a80242 100644 --- a/mat64/matrix_test.go +++ b/mat64/matrix_test.go @@ -46,19 +46,12 @@ func unflatten(r, c int, d []float64) [][]float64 { return m } -func mustDense(m *Dense, err error) *Dense { - if err != nil { - panic(err) - } - return m -} - func eye() *Dense { - return mustDense(NewDense(3, 3, []float64{ + return NewDense(3, 3, []float64{ 1, 0, 0, 0, 1, 0, 0, 0, 1, - })) + }) } func (s *S) TestMaybe(c *check.C) { @@ -185,8 +178,7 @@ func (s *S) TestNewDense(c *check.C) { }}, }, } { - m, err := NewDense(test.rows, test.cols, test.a) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + m := NewDense(test.rows, test.cols, test.a) rows, cols := m.Dims() c.Check(rows, check.Equals, test.rows, check.Commentf("Test %d", i)) c.Check(cols, check.Equals, test.cols, check.Commentf("Test %d", i)) @@ -204,8 +196,7 @@ func (s *S) TestRowCol(c *check.C) { {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}, {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, } { - a, err := NewDense(flatten(af)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(af)) for ri, row := range af { c.Check(a.Row(nil, ri), check.DeepEquals, row, check.Commentf("Test %d", i)) } @@ -220,14 +211,13 @@ func (s *S) TestRowCol(c *check.C) { } func (s *S) TestSetRowColumn(c *check.C) { - for i, as := range [][][]float64{ + for _, as := range [][][]float64{ {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}}, {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, } { for ri, row := range as { - a, err := NewDense(flatten(as)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(as)) t := &Dense{} t.Clone(a) a.SetRow(ri, make([]float64, a.mat.Cols)) @@ -236,8 +226,7 @@ func (s *S) TestSetRowColumn(c *check.C) { } for ci := range as[0] { - a, err := NewDense(flatten(as)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(as)) t := &Dense{} t.Clone(a) a.SetCol(ci, make([]float64, a.mat.Rows)) @@ -281,12 +270,9 @@ func (s *S) TestAdd(c *check.C) { [][]float64{{2, 4, 6}, {8, 10, 12}}, }, } { - a, err := NewDense(flatten(test.a)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - b, err := NewDense(flatten(test.b)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - r, err := NewDense(flatten(test.r)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(test.a)) + b := NewDense(flatten(test.b)) + r := NewDense(flatten(test.r)) temp := &Dense{} temp.Add(a, b) @@ -338,12 +324,9 @@ func (s *S) TestSub(c *check.C) { [][]float64{{0, 0, 0}, {0, 0, 0}}, }, } { - a, err := NewDense(flatten(test.a)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - b, err := NewDense(flatten(test.b)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - r, err := NewDense(flatten(test.r)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(test.a)) + b := NewDense(flatten(test.b)) + r := NewDense(flatten(test.r)) temp := &Dense{} temp.Sub(a, b) @@ -395,12 +378,9 @@ func (s *S) TestMulElem(c *check.C) { [][]float64{{1, 4, 9}, {16, 25, 36}}, }, } { - a, err := NewDense(flatten(test.a)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - b, err := NewDense(flatten(test.b)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - r, err := NewDense(flatten(test.r)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(test.a)) + b := NewDense(flatten(test.b)) + r := NewDense(flatten(test.r)) temp := &Dense{} temp.MulElem(a, b) @@ -457,12 +437,9 @@ func (s *S) TestMul(c *check.C) { [][]float64{{0, 2, 2}, {0, 2, 2}, {0, 2, 2}}, }, } { - a, err := NewDense(flatten(test.a)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - b, err := NewDense(flatten(test.b)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - r, err := NewDense(flatten(test.r)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(test.a)) + b := NewDense(flatten(test.b)) + r := NewDense(flatten(test.r)) temp := &Dense{} temp.Mul(a, b) @@ -587,10 +564,8 @@ func (s *S) TestTranspose(c *check.C) { [][]float64{{1, 4}, {2, 5}, {3, 6}}, }, } { - a, err := NewDense(flatten(test.a)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - t, err := NewDense(flatten(test.t)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(test.a)) + t := NewDense(flatten(test.t)) var r, rr Dense @@ -652,8 +627,7 @@ func (s *S) TestNorm(c *check.C) { norm: 6, }, } { - a, err := NewDense(flatten(test.a)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(test.a)) c.Check(a.Norm(test.ord), check.Equals, test.norm, check.Commentf("Test %d: %v norm = %f", i, test.a, test.norm)) } } @@ -716,10 +690,8 @@ func (s *S) TestApply(c *check.C) { }, }, } { - a, err := NewDense(flatten(test.a)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) - t, err := NewDense(flatten(test.t)) - c.Assert(err, check.Equals, nil, check.Commentf("Test %d", i)) + a := NewDense(flatten(test.a)) + t := NewDense(flatten(test.t)) var r Dense diff --git a/mat64/qr.go b/mat64/qr.go index 1bbda9c3..17ab5e16 100644 --- a/mat64/qr.go +++ b/mat64/qr.go @@ -80,7 +80,7 @@ func (f QRFactor) IsFullRank() bool { func (f QRFactor) H() *Dense { qr := f.QR m, n := qr.Dims() - h, _ := NewDense(m, n, make([]float64, m*n)) + h := NewDense(m, n, nil) for i := 0; i < m; i++ { for j := 0; j < n; j++ { if i >= j { @@ -95,7 +95,7 @@ func (f QRFactor) H() *Dense { func (f QRFactor) R() *Dense { qr, rDiag := f.QR, f.rDiag _, n := qr.Dims() - r, _ := NewDense(n, n, make([]float64, n*n)) + r := NewDense(n, n, nil) for i, v := range rDiag[:n] { for j := 0; j < n; j++ { if i < j { @@ -112,7 +112,7 @@ func (f QRFactor) R() *Dense { func (f QRFactor) Q() *Dense { qr := f.QR m, n := qr.Dims() - q, _ := NewDense(m, n, make([]float64, m*n)) + q := NewDense(m, n, nil) for k := n - 1; k >= 0; k-- { // for i := 0; i < m; i++ { diff --git a/mat64/qr_test.go b/mat64/qr_test.go index 8d23520e..d80f284f 100644 --- a/mat64/qr_test.go +++ b/mat64/qr_test.go @@ -13,7 +13,7 @@ func (s *S) TestQRD(c *check.C) { a *Dense }{ { - a: mustDense(NewDense(4, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})), + a: NewDense(4, 3, []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}), }, } { qf := QR(DenseCopyOf(t.a)) diff --git a/mat64/svd.go b/mat64/svd.go index caa5691b..800698a7 100644 --- a/mat64/svd.go +++ b/mat64/svd.go @@ -40,10 +40,10 @@ func SVD(a *Dense, epsilon, small float64, wantu, wantv bool) SVDFactors { nu := min(m, n) var u, v *Dense if wantu { - u, _ = NewDense(m, nu, make([]float64, m*nu)) + u = NewDense(m, nu, nil) } if wantv { - v, _ = NewDense(n, n, make([]float64, n*n)) + v = NewDense(n, n, nil) } var ( @@ -431,7 +431,7 @@ func SVD(a *Dense, epsilon, small float64, wantu, wantv bool) SVDFactors { // S returns a newly allocated S matrix from the sigma values held by the // factorisation. func (f SVDFactors) S() *Dense { - s, _ := NewDense(len(f.Sigma), len(f.Sigma), make([]float64, len(f.Sigma)*len(f.Sigma))) + s := NewDense(len(f.Sigma), len(f.Sigma), nil) for i, v := range f.Sigma { s.Set(i, i, v) } diff --git a/mat64/svd_test.go b/mat64/svd_test.go index 7484cb89..2cef6b86 100644 --- a/mat64/svd_test.go +++ b/mat64/svd_test.go @@ -25,47 +25,47 @@ func (s *S) TestSVD(c *check.C) { v *Dense }{ { - a: mustDense(NewDense(4, 2, []float64{2, 4, 1, 3, 0, 0, 0, 0})), + a: NewDense(4, 2, []float64{2, 4, 1, 3, 0, 0, 0, 0}), epsilon: math.Pow(2, -52.0), small: math.Pow(2, -966.0), wantu: true, - u: mustDense(NewDense(4, 2, []float64{ + u: NewDense(4, 2, []float64{ 0.8174155604703632, -0.5760484367663209, 0.5760484367663209, 0.8174155604703633, 0, 0, 0, 0, - })), + }), sigma: []float64{5.464985704219041, 0.365966190626258}, wantv: true, - v: mustDense(NewDense(2, 2, []float64{ + v: NewDense(2, 2, []float64{ 0.4045535848337571, -0.9145142956773044, 0.9145142956773044, 0.4045535848337571, - })), + }), }, { - a: mustDense(NewDense(4, 2, []float64{2, 4, 1, 3, 0, 0, 0, 0})), + a: NewDense(4, 2, []float64{2, 4, 1, 3, 0, 0, 0, 0}), epsilon: math.Pow(2, -52.0), small: math.Pow(2, -966.0), wantu: true, - u: mustDense(NewDense(4, 2, []float64{ + u: NewDense(4, 2, []float64{ 0.8174155604703632, -0.5760484367663209, 0.5760484367663209, 0.8174155604703633, 0, 0, 0, 0, - })), + }), sigma: []float64{5.464985704219041, 0.365966190626258}, wantv: false, }, { - a: mustDense(NewDense(4, 2, []float64{2, 4, 1, 3, 0, 0, 0, 0})), + a: NewDense(4, 2, []float64{2, 4, 1, 3, 0, 0, 0, 0}), epsilon: math.Pow(2, -52.0), small: math.Pow(2, -966.0), @@ -75,13 +75,13 @@ func (s *S) TestSVD(c *check.C) { sigma: []float64{5.464985704219041, 0.365966190626258}, wantv: true, - v: mustDense(NewDense(2, 2, []float64{ + v: NewDense(2, 2, []float64{ 0.4045535848337571, -0.9145142956773044, 0.9145142956773044, 0.4045535848337571, - })), + }), }, { - a: mustDense(NewDense(4, 2, []float64{2, 4, 1, 3, 0, 0, 0, 0})), + a: NewDense(4, 2, []float64{2, 4, 1, 3, 0, 0, 0, 0}), epsilon: math.Pow(2, -52.0), small: math.Pow(2, -966.0), @@ -95,11 +95,11 @@ func (s *S) TestSVD(c *check.C) { // forcing a to be a tall or square matrix. // // This is a failing case to use to fix that bug. - a: mustDense(NewDense(3, 11, []float64{ + a: NewDense(3, 11, []float64{ 1, 1, 0, 1, 0, 0, 0, 0, 0, 11, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 12, 2, 1, 1, 0, 0, 0, 0, 0, 0, 1, 13, 3, - })), + }), epsilon: math.Pow(2, -52.0), small: math.Pow(2, -966.0),