| // Copyright ©2015 The Gonum Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package mat |
| |
| import ( |
| "fmt" |
| "os" |
| "reflect" |
| "testing" |
| |
| "golang.org/x/exp/rand" |
| |
| "gonum.org/v1/gonum/blas" |
| "gonum.org/v1/gonum/blas/blas64" |
| "gonum.org/v1/gonum/floats/scalar" |
| ) |
| |
| func TestNewSymmetric(t *testing.T) { |
| t.Parallel() |
| for i, test := range []struct { |
| data []float64 |
| n int |
| mat *SymDense |
| }{ |
| { |
| data: []float64{ |
| 1, 2, 3, |
| 4, 5, 6, |
| 7, 8, 9, |
| }, |
| n: 3, |
| mat: &SymDense{ |
| mat: blas64.Symmetric{ |
| N: 3, |
| Stride: 3, |
| Uplo: blas.Upper, |
| Data: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}, |
| }, |
| cap: 3, |
| }, |
| }, |
| } { |
| sym := NewSymDense(test.n, test.data) |
| rows, cols := sym.Dims() |
| |
| if rows != test.n { |
| t.Errorf("unexpected number of rows for test %d: got: %d want: %d", i, rows, test.n) |
| } |
| if cols != test.n { |
| t.Errorf("unexpected number of cols for test %d: got: %d want: %d", i, cols, test.n) |
| } |
| if !reflect.DeepEqual(sym, test.mat) { |
| t.Errorf("unexpected data slice for test %d: got: %v want: %v", i, sym, test.mat) |
| } |
| |
| m := NewDense(test.n, test.n, test.data) |
| if !reflect.DeepEqual(sym.mat.Data, m.mat.Data) { |
| t.Errorf("unexpected data slice mismatch for test %d: got: %v want: %v", i, sym.mat.Data, m.mat.Data) |
| } |
| } |
| |
| panicked, message := panics(func() { NewSymDense(3, []float64{1, 2}) }) |
| if !panicked || message != ErrShape.Error() { |
| t.Error("expected panic for invalid data slice length") |
| } |
| } |
| |
| func TestSymAtSet(t *testing.T) { |
| t.Parallel() |
| sym := &SymDense{ |
| mat: blas64.Symmetric{ |
| N: 3, |
| Stride: 3, |
| Uplo: blas.Upper, |
| Data: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}, |
| }, |
| cap: 3, |
| } |
| rows, cols := sym.Dims() |
| |
| // Check At out of bounds |
| for _, row := range []int{-1, rows, rows + 1} { |
| panicked, message := panics(func() { sym.At(row, 0) }) |
| if !panicked || message != ErrRowAccess.Error() { |
| t.Errorf("expected panic for invalid row access N=%d r=%d", rows, row) |
| } |
| } |
| for _, col := range []int{-1, cols, cols + 1} { |
| panicked, message := panics(func() { sym.At(0, col) }) |
| if !panicked || message != ErrColAccess.Error() { |
| t.Errorf("expected panic for invalid column access N=%d c=%d", cols, col) |
| } |
| } |
| |
| // Check Set out of bounds |
| for _, row := range []int{-1, rows, rows + 1} { |
| panicked, message := panics(func() { sym.SetSym(row, 0, 1.2) }) |
| if !panicked || message != ErrRowAccess.Error() { |
| t.Errorf("expected panic for invalid row access N=%d r=%d", rows, row) |
| } |
| } |
| for _, col := range []int{-1, cols, cols + 1} { |
| panicked, message := panics(func() { sym.SetSym(0, col, 1.2) }) |
| if !panicked || message != ErrColAccess.Error() { |
| t.Errorf("expected panic for invalid column access N=%d c=%d", cols, col) |
| } |
| } |
| |
| for _, st := range []struct { |
| row, col int |
| orig, new float64 |
| }{ |
| {row: 1, col: 2, orig: 6, new: 15}, |
| {row: 2, col: 1, orig: 15, new: 12}, |
| } { |
| if e := sym.At(st.row, st.col); e != st.orig { |
| t.Errorf("unexpected value for At(%d, %d): got: %v want: %v", st.row, st.col, e, st.orig) |
| } |
| if e := sym.At(st.col, st.row); e != st.orig { |
| t.Errorf("unexpected value for At(%d, %d): got: %v want: %v", st.col, st.row, e, st.orig) |
| } |
| sym.SetSym(st.row, st.col, st.new) |
| if e := sym.At(st.row, st.col); e != st.new { |
| t.Errorf("unexpected value for At(%d, %d) after SetSym(%[1]d, %[2]d, %[4]v): got: %[3]v want: %v", st.row, st.col, e, st.new) |
| } |
| if e := sym.At(st.col, st.row); e != st.new { |
| t.Errorf("unexpected value for At(%d, %d) after SetSym(%[2]d, %[1]d, %[4]v): got: %[3]v want: %v", st.col, st.row, e, st.new) |
| } |
| } |
| } |
| |
| func TestSymDenseZero(t *testing.T) { |
| t.Parallel() |
| // Elements that equal 1 should be set to zero, elements that equal -1 |
| // should remain unchanged. |
| for _, test := range []*SymDense{ |
| { |
| mat: blas64.Symmetric{ |
| Uplo: blas.Upper, |
| N: 4, |
| Stride: 5, |
| Data: []float64{ |
| 1, 1, 1, 1, -1, |
| -1, 1, 1, 1, -1, |
| -1, -1, 1, 1, -1, |
| -1, -1, -1, 1, -1, |
| }, |
| }, |
| }, |
| } { |
| dataCopy := make([]float64, len(test.mat.Data)) |
| copy(dataCopy, test.mat.Data) |
| test.Zero() |
| for i, v := range test.mat.Data { |
| if dataCopy[i] != -1 && v != 0 { |
| t.Errorf("Matrix not zeroed in bounds") |
| } |
| if dataCopy[i] == -1 && v != -1 { |
| t.Errorf("Matrix zeroed out of bounds") |
| } |
| } |
| } |
| } |
| |
| func TestSymDiagView(t *testing.T) { |
| t.Parallel() |
| for cas, test := range []*SymDense{ |
| NewSymDense(1, []float64{1}), |
| NewSymDense(2, []float64{1, 2, 2, 3}), |
| NewSymDense(3, []float64{1, 2, 3, 2, 4, 5, 3, 5, 6}), |
| } { |
| testDiagView(t, cas, test) |
| } |
| } |
| |
| func TestSymAdd(t *testing.T) { |
| t.Parallel() |
| rnd := rand.New(rand.NewSource(1)) |
| for _, test := range []struct { |
| n int |
| }{ |
| {n: 1}, |
| {n: 2}, |
| {n: 3}, |
| {n: 4}, |
| {n: 5}, |
| {n: 10}, |
| } { |
| n := test.n |
| a := NewSymDense(n, nil) |
| for i := range a.mat.Data { |
| a.mat.Data[i] = rnd.Float64() |
| } |
| b := NewSymDense(n, nil) |
| for i := range a.mat.Data { |
| b.mat.Data[i] = rnd.Float64() |
| } |
| var m Dense |
| m.Add(a, b) |
| |
| // Check with new receiver |
| var s SymDense |
| s.AddSym(a, b) |
| for i := 0; i < n; i++ { |
| for j := i; j < n; j++ { |
| want := m.At(i, j) |
| if got := s.At(i, j); got != want { |
| t.Errorf("unexpected value for At(%d, %d): got: %v want: %v", i, j, got, want) |
| } |
| } |
| } |
| |
| // Check with equal receiver |
| s.CopySym(a) |
| s.AddSym(&s, b) |
| for i := 0; i < n; i++ { |
| for j := i; j < n; j++ { |
| want := m.At(i, j) |
| if got := s.At(i, j); got != want { |
| t.Errorf("unexpected value for At(%d, %d): got: %v want: %v", i, j, got, want) |
| } |
| } |
| } |
| } |
| |
| method := func(receiver, a, b Matrix) { |
| type addSymer interface { |
| AddSym(a, b Symmetric) |
| } |
| rd := receiver.(addSymer) |
| rd.AddSym(a.(Symmetric), b.(Symmetric)) |
| } |
| denseComparison := func(receiver, a, b *Dense) { |
| receiver.Add(a, b) |
| } |
| testTwoInput(t, "AddSym", &SymDense{}, method, denseComparison, legalTypesSym, legalSizeSameSquare, 1e-14) |
| } |
| |
| func TestCopy(t *testing.T) { |
| t.Parallel() |
| rnd := rand.New(rand.NewSource(1)) |
| for _, test := range []struct { |
| n int |
| }{ |
| {n: 1}, |
| {n: 2}, |
| {n: 3}, |
| {n: 4}, |
| {n: 5}, |
| {n: 10}, |
| } { |
| n := test.n |
| a := NewSymDense(n, nil) |
| for i := range a.mat.Data { |
| a.mat.Data[i] = rnd.Float64() |
| } |
| s := NewSymDense(n, nil) |
| s.CopySym(a) |
| for i := 0; i < n; i++ { |
| for j := i; j < n; j++ { |
| want := a.At(i, j) |
| if got := s.At(i, j); got != want { |
| t.Errorf("unexpected value for At(%d, %d): got: %v want: %v", i, j, got, want) |
| } |
| } |
| } |
| } |
| } |
| |
| // TODO(kortschak) Roll this into testOneInput when it exists. |
| // https://github.com/gonum/matrix/issues/171 |
| func TestSymCopyPanic(t *testing.T) { |
| t.Parallel() |
| var ( |
| a SymDense |
| n int |
| ) |
| m := NewSymDense(1, nil) |
| panicked, message := panics(func() { n = m.CopySym(&a) }) |
| if panicked { |
| t.Errorf("unexpected panic: %v", message) |
| } |
| if n != 0 { |
| t.Errorf("unexpected n: got: %d want: 0", n) |
| } |
| } |
| |
| func TestSymRankOne(t *testing.T) { |
| t.Parallel() |
| rnd := rand.New(rand.NewSource(1)) |
| const tol = 1e-15 |
| |
| for _, test := range []struct { |
| n int |
| }{ |
| {n: 1}, |
| {n: 2}, |
| {n: 3}, |
| {n: 4}, |
| {n: 5}, |
| {n: 10}, |
| } { |
| n := test.n |
| alpha := 2.0 |
| a := NewSymDense(n, nil) |
| for i := range a.mat.Data { |
| a.mat.Data[i] = rnd.Float64() |
| } |
| x := make([]float64, n) |
| for i := range x { |
| x[i] = rnd.Float64() |
| } |
| |
| xMat := NewDense(n, 1, x) |
| var m Dense |
| m.Mul(xMat, xMat.T()) |
| m.Scale(alpha, &m) |
| m.Add(&m, a) |
| |
| // Check with new receiver |
| s := NewSymDense(n, nil) |
| s.SymRankOne(a, alpha, NewVecDense(len(x), x)) |
| for i := 0; i < n; i++ { |
| for j := i; j < n; j++ { |
| want := m.At(i, j) |
| if got := s.At(i, j); !scalar.EqualWithinAbsOrRel(got, want, tol, tol) { |
| t.Errorf("unexpected value for At(%d, %d): got: %v want: %v", i, j, got, want) |
| } |
| } |
| } |
| |
| // Check with reused receiver |
| copy(s.mat.Data, a.mat.Data) |
| s.SymRankOne(s, alpha, NewVecDense(len(x), x)) |
| for i := 0; i < n; i++ { |
| for j := i; j < n; j++ { |
| want := m.At(i, j) |
| if got := s.At(i, j); !scalar.EqualWithinAbsOrRel(got, want, tol, tol) { |
| t.Errorf("unexpected value for At(%d, %d): got: %v want: %v", i, j, got, want) |
| } |
| } |
| } |
| } |
| |
| alpha := 3.0 |
| method := func(receiver, a, b Matrix) { |
| type SymRankOner interface { |
| SymRankOne(a Symmetric, alpha float64, x Vector) |
| } |
| rd := receiver.(SymRankOner) |
| rd.SymRankOne(a.(Symmetric), alpha, b.(Vector)) |
| } |
| denseComparison := func(receiver, a, b *Dense) { |
| var tmp Dense |
| tmp.Mul(b, b.T()) |
| tmp.Scale(alpha, &tmp) |
| receiver.Add(a, &tmp) |
| } |
| legalTypes := func(a, b Matrix) bool { |
| _, ok := a.(Symmetric) |
| if !ok { |
| return false |
| } |
| _, ok = b.(Vector) |
| return ok |
| } |
| legalSize := func(ar, ac, br, bc int) bool { |
| if ar != ac { |
| return false |
| } |
| return br == ar |
| } |
| testTwoInput(t, "SymRankOne", &SymDense{}, method, denseComparison, legalTypes, legalSize, 1e-14) |
| } |
| |
| func TestIssue250SymRankOne(t *testing.T) { |
| t.Parallel() |
| x := NewVecDense(5, []float64{1, 2, 3, 4, 5}) |
| var s1, s2 SymDense |
| s1.SymRankOne(NewSymDense(5, nil), 1, x) |
| s2.SymRankOne(NewSymDense(5, nil), 1, x) |
| s2.SymRankOne(NewSymDense(5, nil), 1, x) |
| if !Equal(&s1, &s2) { |
| t.Error("unexpected result from repeat") |
| } |
| } |
| |
| func TestRankTwo(t *testing.T) { |
| t.Parallel() |
| rnd := rand.New(rand.NewSource(1)) |
| for _, test := range []struct { |
| n int |
| }{ |
| {n: 1}, |
| {n: 2}, |
| {n: 3}, |
| {n: 4}, |
| {n: 5}, |
| {n: 10}, |
| } { |
| n := test.n |
| alpha := 2.0 |
| a := NewSymDense(n, nil) |
| for i := range a.mat.Data { |
| a.mat.Data[i] = rnd.Float64() |
| } |
| x := make([]float64, n) |
| y := make([]float64, n) |
| for i := range x { |
| x[i] = rnd.Float64() |
| y[i] = rnd.Float64() |
| } |
| |
| xMat := NewDense(n, 1, x) |
| yMat := NewDense(n, 1, y) |
| var m Dense |
| m.Mul(xMat, yMat.T()) |
| var tmp Dense |
| tmp.Mul(yMat, xMat.T()) |
| m.Add(&m, &tmp) |
| m.Scale(alpha, &m) |
| m.Add(&m, a) |
| |
| // Check with new receiver |
| s := NewSymDense(n, nil) |
| s.RankTwo(a, alpha, NewVecDense(len(x), x), NewVecDense(len(y), y)) |
| for i := 0; i < n; i++ { |
| for j := i; j < n; j++ { |
| if !scalar.EqualWithinAbsOrRel(s.At(i, j), m.At(i, j), 1e-14, 1e-14) { |
| t.Errorf("unexpected element value at (%d,%d): got: %f want: %f", i, j, m.At(i, j), s.At(i, j)) |
| } |
| } |
| } |
| |
| // Check with reused receiver |
| copy(s.mat.Data, a.mat.Data) |
| s.RankTwo(s, alpha, NewVecDense(len(x), x), NewVecDense(len(y), y)) |
| for i := 0; i < n; i++ { |
| for j := i; j < n; j++ { |
| if !scalar.EqualWithinAbsOrRel(s.At(i, j), m.At(i, j), 1e-14, 1e-14) { |
| t.Errorf("unexpected element value at (%d,%d): got: %f want: %f", i, j, m.At(i, j), s.At(i, j)) |
| } |
| } |
| } |
| } |
| } |
| |
| func TestSymRankK(t *testing.T) { |
| t.Parallel() |
| alpha := 3.0 |
| method := func(receiver, a, b Matrix) { |
| type SymRankKer interface { |
| SymRankK(a Symmetric, alpha float64, x Matrix) |
| } |
| rd := receiver.(SymRankKer) |
| rd.SymRankK(a.(Symmetric), alpha, b) |
| } |
| denseComparison := func(receiver, a, b *Dense) { |
| var tmp Dense |
| tmp.Mul(b, b.T()) |
| tmp.Scale(alpha, &tmp) |
| receiver.Add(a, &tmp) |
| } |
| legalTypes := func(a, b Matrix) bool { |
| _, ok := a.(Symmetric) |
| return ok |
| } |
| legalSize := func(ar, ac, br, bc int) bool { |
| if ar != ac { |
| return false |
| } |
| return br == ar |
| } |
| testTwoInput(t, "SymRankK", &SymDense{}, method, denseComparison, legalTypes, legalSize, 1e-14) |
| } |
| |
| func TestSymOuterK(t *testing.T) { |
| t.Parallel() |
| for _, f := range []float64{0.5, 1, 3} { |
| method := func(receiver, x Matrix) { |
| type SymOuterKer interface { |
| SymOuterK(alpha float64, x Matrix) |
| } |
| rd := receiver.(SymOuterKer) |
| rd.SymOuterK(f, x) |
| } |
| denseComparison := func(receiver, x *Dense) { |
| receiver.Mul(x, x.T()) |
| receiver.Scale(f, receiver) |
| } |
| testOneInput(t, "SymOuterK", &SymDense{}, method, denseComparison, isAnyType, isAnySize, 1e-14) |
| } |
| } |
| |
| func TestIssue250SymOuterK(t *testing.T) { |
| t.Parallel() |
| x := NewVecDense(5, []float64{1, 2, 3, 4, 5}) |
| var s1, s2 SymDense |
| s1.SymOuterK(1, x) |
| s2.SymOuterK(1, x) |
| s2.SymOuterK(1, x) |
| if !Equal(&s1, &s2) { |
| t.Error("unexpected result from repeat") |
| } |
| } |
| |
| func TestScaleSym(t *testing.T) { |
| t.Parallel() |
| for _, f := range []float64{0.5, 1, 3} { |
| method := func(receiver, a Matrix) { |
| type ScaleSymer interface { |
| ScaleSym(f float64, a Symmetric) |
| } |
| rd := receiver.(ScaleSymer) |
| rd.ScaleSym(f, a.(Symmetric)) |
| } |
| denseComparison := func(receiver, a *Dense) { |
| receiver.Scale(f, a) |
| } |
| testOneInput(t, "ScaleSym", &SymDense{}, method, denseComparison, legalTypeSym, isSquare, 1e-14) |
| } |
| } |
| |
| func TestSubsetSym(t *testing.T) { |
| t.Parallel() |
| for _, test := range []struct { |
| a *SymDense |
| dims []int |
| ans *SymDense |
| }{ |
| { |
| a: NewSymDense(3, []float64{ |
| 1, 2, 3, |
| 0, 4, 5, |
| 0, 0, 6, |
| }), |
| dims: []int{0, 2}, |
| ans: NewSymDense(2, []float64{ |
| 1, 3, |
| 0, 6, |
| }), |
| }, |
| { |
| a: NewSymDense(3, []float64{ |
| 1, 2, 3, |
| 0, 4, 5, |
| 0, 0, 6, |
| }), |
| dims: []int{2, 0}, |
| ans: NewSymDense(2, []float64{ |
| 6, 3, |
| 0, 1, |
| }), |
| }, |
| { |
| a: NewSymDense(3, []float64{ |
| 1, 2, 3, |
| 0, 4, 5, |
| 0, 0, 6, |
| }), |
| dims: []int{1, 1, 1}, |
| ans: NewSymDense(3, []float64{ |
| 4, 4, 4, |
| 0, 4, 4, |
| 0, 0, 4, |
| }), |
| }, |
| } { |
| var s SymDense |
| s.SubsetSym(test.a, test.dims) |
| if !Equal(&s, test.ans) { |
| t.Errorf("SubsetSym mismatch dims %v\nGot:\n% v\nWant:\n% v\n", test.dims, s, test.ans) |
| } |
| } |
| |
| dims := []int{0, 2} |
| maxDim := dims[0] |
| for _, v := range dims { |
| if maxDim < v { |
| maxDim = v |
| } |
| } |
| method := func(receiver, a Matrix) { |
| type SubsetSymer interface { |
| SubsetSym(a Symmetric, set []int) |
| } |
| rd := receiver.(SubsetSymer) |
| rd.SubsetSym(a.(Symmetric), dims) |
| } |
| denseComparison := func(receiver, a *Dense) { |
| *receiver = *NewDense(len(dims), len(dims), nil) |
| sz := len(dims) |
| for i := 0; i < sz; i++ { |
| for j := 0; j < sz; j++ { |
| receiver.Set(i, j, a.At(dims[i], dims[j])) |
| } |
| } |
| } |
| legalSize := func(ar, ac int) bool { |
| return ar == ac && ar > maxDim |
| } |
| |
| testOneInput(t, "SubsetSym", &SymDense{}, method, denseComparison, legalTypeSym, legalSize, 0) |
| } |
| |
| func TestViewGrowSquare(t *testing.T) { |
| t.Parallel() |
| // n is the size of the original SymDense. |
| // The first view uses start1, span1. The second view uses start2, span2 on |
| // the first view. |
| for _, test := range []struct { |
| n, start1, span1, start2, span2 int |
| }{ |
| {10, 0, 10, 0, 10}, |
| {10, 0, 8, 0, 8}, |
| {10, 2, 8, 0, 6}, |
| {10, 2, 7, 4, 2}, |
| {10, 2, 6, 0, 5}, |
| } { |
| n := test.n |
| s := NewSymDense(n, nil) |
| for i := 0; i < n; i++ { |
| for j := i; j < n; j++ { |
| s.SetSym(i, j, float64((i+1)*n+j+1)) |
| } |
| } |
| |
| // Take a subset and check the view matches. |
| start1 := test.start1 |
| span1 := test.span1 |
| v := s.sliceSym(start1, start1+span1) |
| for i := 0; i < span1; i++ { |
| for j := i; j < span1; j++ { |
| if v.At(i, j) != s.At(start1+i, start1+j) { |
| t.Errorf("View mismatch") |
| } |
| } |
| } |
| |
| start2 := test.start2 |
| span2 := test.span2 |
| v2 := v.SliceSym(start2, start2+span2).(*SymDense) |
| |
| for i := 0; i < span2; i++ { |
| for j := i; j < span2; j++ { |
| if v2.At(i, j) != s.At(start1+start2+i, start1+start2+j) { |
| t.Errorf("Second view mismatch") |
| } |
| } |
| } |
| |
| // Check that a write to the view is reflected in the original. |
| v2.SetSym(0, 0, 1.2) |
| if s.At(start1+start2, start1+start2) != 1.2 { |
| t.Errorf("Write to view not reflected in original") |
| } |
| |
| // Grow the matrix back to the original view |
| gn := n - start1 - start2 |
| g := v2.GrowSym(gn - v2.Symmetric()).(*SymDense) |
| g.SetSym(1, 1, 2.2) |
| |
| for i := 0; i < gn; i++ { |
| for j := 0; j < gn; j++ { |
| if g.At(i, j) != s.At(start1+start2+i, start1+start2+j) { |
| t.Errorf("Grow mismatch") |
| |
| fmt.Printf("g=\n% v\n", Formatted(g)) |
| fmt.Printf("s=\n% v\n", Formatted(s)) |
| os.Exit(1) |
| } |
| } |
| } |
| |
| // View g, then grow it and make sure all the elements were copied. |
| gv := g.SliceSym(0, gn-1).(*SymDense) |
| |
| gg := gv.GrowSym(2) |
| for i := 0; i < gn; i++ { |
| for j := 0; j < gn; j++ { |
| if g.At(i, j) != gg.At(i, j) { |
| t.Errorf("Expand mismatch") |
| } |
| } |
| } |
| |
| s.Reset() |
| rg := s.GrowSym(n).(*SymDense) |
| if rg.mat.Stride < n { |
| t.Errorf("unexpected stride after GrowSym on empty matrix: got:%d want >= %d", rg.mat.Stride, n) |
| } |
| } |
| } |
| |
| func TestPowPSD(t *testing.T) { |
| t.Parallel() |
| for cas, test := range []struct { |
| a *SymDense |
| pow float64 |
| ans *SymDense |
| }{ |
| // Comparison with Matlab. |
| { |
| a: NewSymDense(2, []float64{10, 5, 5, 12}), |
| pow: 0.5, |
| ans: NewSymDense(2, []float64{3.065533767740645, 0.776210486171016, 0.776210486171016, 3.376017962209052}), |
| }, |
| { |
| a: NewSymDense(2, []float64{11, -1, -1, 8}), |
| pow: 0.5, |
| ans: NewSymDense(2, []float64{3.312618742210524, -0.162963396980939, -0.162963396980939, 2.823728551267709}), |
| }, |
| { |
| a: NewSymDense(2, []float64{10, 5, 5, 12}), |
| pow: -0.5, |
| ans: NewSymDense(2, []float64{0.346372134547712, -0.079637515547296, -0.079637515547296, 0.314517128328794}), |
| }, |
| { |
| a: NewSymDense(3, []float64{15, -1, -3, -1, 8, 6, -3, 6, 14}), |
| pow: 0.6, |
| ans: NewSymDense(3, []float64{ |
| 5.051214323034288, -0.163162161893975, -0.612153996497505, |
| -0.163162161893976, 3.283474884617009, 1.432842761381493, |
| -0.612153996497505, 1.432842761381494, 4.695873060862573, |
| }), |
| }, |
| } { |
| var s SymDense |
| err := s.PowPSD(test.a, test.pow) |
| if err != nil { |
| panic("bad test") |
| } |
| if !EqualApprox(&s, test.ans, 1e-10) { |
| t.Errorf("Case %d, pow mismatch", cas) |
| fmt.Println(Formatted(&s)) |
| fmt.Println(Formatted(test.ans)) |
| } |
| } |
| |
| // Compare with Dense.Pow |
| rnd := rand.New(rand.NewSource(1)) |
| for dim := 2; dim < 10; dim++ { |
| for pow := 2; pow < 6; pow++ { |
| a := NewDense(dim, dim, nil) |
| for i := 0; i < dim; i++ { |
| for j := 0; j < dim; j++ { |
| a.Set(i, j, rnd.Float64()) |
| } |
| } |
| var mat SymDense |
| mat.SymOuterK(1, a) |
| |
| var sym SymDense |
| err := sym.PowPSD(&mat, float64(pow)) |
| if err != nil { |
| t.Errorf("unexpected error: %v", err) |
| } |
| |
| var dense Dense |
| dense.Pow(&mat, pow) |
| |
| if !EqualApprox(&sym, &dense, 1e-10) { |
| t.Errorf("Dim %d: pow mismatch", dim) |
| } |
| } |
| } |
| } |
| |
| func BenchmarkSymSum1000(b *testing.B) { symSumBench(b, 1000) } |
| |
| var symSumForBench float64 |
| |
| func symSumBench(b *testing.B, size int) { |
| src := rand.NewSource(1) |
| a := randSymDense(size, src) |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| symSumForBench = Sum(a) |
| } |
| } |
| |
| func randSymDense(size int, src rand.Source) *SymDense { |
| rnd := rand.New(src) |
| backData := make([]float64, size*size) |
| for i := 0; i < size; i++ { |
| backData[i*size+i] = rnd.Float64() |
| for j := i + 1; j < size; j++ { |
| v := rnd.Float64() |
| backData[i*size+j] = v |
| backData[j*size+i] = v |
| } |
| } |
| s := NewSymDense(size, backData) |
| return s |
| } |