From bcbf6c8e4e5292073ce14c6cf4b01cfe83743b00 Mon Sep 17 00:00:00 2001 From: Brendan Tracey Date: Sun, 27 Jan 2019 22:16:06 +0000 Subject: [PATCH] mat: Add Zero method to reset values of matrices (#819) * mat: Add Zero method to reset values of matrices --- mat/band.go | 12 ++++++++++ mat/band_test.go | 53 ++++++++++++++++++++++++++++++++++++++++++ mat/dense.go | 7 ++++++ mat/dense_test.go | 32 +++++++++++++++++++++++++ mat/diagonal.go | 7 ++++++ mat/diagonal_test.go | 32 +++++++++++++++++++++++++ mat/symband.go | 8 +++++++ mat/symband_test.go | 35 ++++++++++++++++++++++++++++ mat/symmetric.go | 7 ++++++ mat/symmetric_test.go | 32 +++++++++++++++++++++++++ mat/triangular.go | 13 +++++++++++ mat/triangular_test.go | 45 +++++++++++++++++++++++++++++++++++ mat/triband.go | 15 ++++++++++++ mat/triband_test.go | 51 ++++++++++++++++++++++++++++++++++++++++ mat/vector.go | 7 ++++++ mat/vector_test.go | 32 +++++++++++++++++++++++++ 16 files changed, 388 insertions(+) diff --git a/mat/band.go b/mat/band.go index 685d96a8..20225109 100644 --- a/mat/band.go +++ b/mat/band.go @@ -242,3 +242,15 @@ func (b *BandDense) DoColNonZero(j int, fn func(i, j int, v float64)) { } } } + +// Zero sets all of the matrix elements to zero. +func (b *BandDense) Zero() { + m := b.mat.Rows + kL := b.mat.KL + nCol := b.mat.KU + 1 + kL + for i := 0; i < m; i++ { + l := max(0, kL-i) + u := min(nCol, m+kL-i) + zero(b.mat.Data[i*b.mat.Stride+l : i*b.mat.Stride+u]) + } +} diff --git a/mat/band_test.go b/mat/band_test.go index 3ef4d4eb..3d7674a1 100644 --- a/mat/band_test.go +++ b/mat/band_test.go @@ -248,6 +248,59 @@ func TestNewDiagonalRect(t *testing.T) { } } +func TestBandDenseZero(t *testing.T) { + // Elements that equal 1 should be set to zero, elements that equal -1 + // should remain unchanged. + for _, test := range []*BandDense{ + &BandDense{ + mat: blas64.Band{ + Rows: 6, + Cols: 7, + Stride: 8, + KL: 1, + KU: 2, + Data: []float64{ + -1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, -1, -1, -1, -1, -1, + 1, 1, -1, -1, -1, -1, -1, -1, + }, + }, + }, + &BandDense{ + mat: blas64.Band{ + Rows: 6, + Cols: 7, + Stride: 8, + KL: 2, + KU: 1, + Data: []float64{ + -1, -1, 1, 1, -1, -1, -1, -1, + -1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, 1, -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 TestBandDiagView(t *testing.T) { for cas, test := range []*BandDense{ NewBandDense(1, 1, 0, 0, []float64{1}), diff --git a/mat/dense.go b/mat/dense.go index 01beb55d..be4ba8bb 100644 --- a/mat/dense.go +++ b/mat/dense.go @@ -122,6 +122,13 @@ func (m *Dense) reuseAsZeroed(r, c int) { if r != m.mat.Rows || c != m.mat.Cols { panic(ErrShape) } + m.Zero() +} + +// Zero sets all of the matrix elements to zero. +func (m *Dense) Zero() { + r := m.mat.Rows + c := m.mat.Cols for i := 0; i < r; i++ { zero(m.mat.Data[i*m.mat.Stride : i*m.mat.Stride+c]) } diff --git a/mat/dense_test.go b/mat/dense_test.go index 59dca816..ea0204a0 100644 --- a/mat/dense_test.go +++ b/mat/dense_test.go @@ -257,6 +257,38 @@ func TestSetRowColumn(t *testing.T) { } } +func TestDenseZero(t *testing.T) { + // Elements that equal 1 should be set to zero, elements that equal -1 + // should remain unchanged. + for _, test := range []*Dense{ + &Dense{ + mat: blas64.General{ + Rows: 4, + Cols: 3, + 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 TestRowColView(t *testing.T) { for _, test := range []struct { mat [][]float64 diff --git a/mat/diagonal.go b/mat/diagonal.go index 237b53d1..e9f074a7 100644 --- a/mat/diagonal.go +++ b/mat/diagonal.go @@ -168,6 +168,13 @@ func (d *DiagDense) Reset() { d.mat.Data = d.mat.Data[:0] } +// Zero sets all of the matrix elements to zero. +func (d *DiagDense) Zero() { + for i := 0; i < d.mat.N; i++ { + d.mat.Data[d.mat.Inc*i] = 0 + } +} + // DiagView returns the diagonal as a matrix backed by the original data. func (d *DiagDense) DiagView() Diagonal { return d diff --git a/mat/diagonal_test.go b/mat/diagonal_test.go index 717600c7..b21e7d1b 100644 --- a/mat/diagonal_test.go +++ b/mat/diagonal_test.go @@ -56,6 +56,38 @@ func TestNewDiagDense(t *testing.T) { } } +func TestDiagDenseZero(t *testing.T) { + // Elements that equal 1 should be set to zero, elements that equal -1 + // should remain unchanged. + for _, test := range []*DiagDense{ + &DiagDense{ + mat: blas64.Vector{ + N: 5, + Inc: 2, + Data: []float64{ + 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 TestDiagonalStride(t *testing.T) { for _, test := range []struct { diag *DiagDense diff --git a/mat/symband.go b/mat/symband.go index 9aa735a2..a6b71030 100644 --- a/mat/symband.go +++ b/mat/symband.go @@ -145,6 +145,14 @@ func (s *SymBandDense) RawSymBand() blas64.SymmetricBand { return s.mat } +// Zero sets all of the matrix elements to zero. +func (s *SymBandDense) Zero() { + for i := 0; i < s.mat.N; i++ { + u := min(1+s.mat.K, s.mat.N-i) + zero(s.mat.Data[i*s.mat.Stride : i*s.mat.Stride+u]) + } +} + // DiagView returns the diagonal as a matrix backed by the original data. func (s *SymBandDense) DiagView() Diagonal { n := s.mat.N diff --git a/mat/symband_test.go b/mat/symband_test.go index d8e17314..5be6c53f 100644 --- a/mat/symband_test.go +++ b/mat/symband_test.go @@ -195,3 +195,38 @@ func TestSymBandDiagView(t *testing.T) { testDiagView(t, cas, test) } } + +func TestSymBandDenseZero(t *testing.T) { + // Elements that equal 1 should be set to zero, elements that equal -1 + // should remain unchanged. + for _, test := range []*SymBandDense{ + &SymBandDense{ + mat: blas64.SymmetricBand{ + Uplo: blas.Upper, + N: 6, + K: 2, + Stride: 5, + Data: []float64{ + 1, 1, 1, -1, -1, + 1, 1, 1, -1, -1, + 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") + } + } + } +} diff --git a/mat/symmetric.go b/mat/symmetric.go index 5e4ce192..a060105a 100644 --- a/mat/symmetric.go +++ b/mat/symmetric.go @@ -131,6 +131,13 @@ func (s *SymDense) Reset() { s.mat.Data = s.mat.Data[:0] } +// Zero sets all of the matrix elements to zero. +func (s *SymDense) Zero() { + for i := 0; i < s.mat.N; i++ { + zero(s.mat.Data[i*s.mat.Stride+i : i*s.mat.Stride+s.mat.N]) + } +} + // IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the // receiver for size-restricted operations. SymDense matrices can be zeroed using Reset. func (s *SymDense) IsZero() bool { diff --git a/mat/symmetric_test.go b/mat/symmetric_test.go index 15cd4e61..20dd4361 100644 --- a/mat/symmetric_test.go +++ b/mat/symmetric_test.go @@ -129,6 +129,38 @@ func TestSymAtSet(t *testing.T) { } } +func TestSymDenseZero(t *testing.T) { + // Elements that equal 1 should be set to zero, elements that equal -1 + // should remain unchanged. + for _, test := range []*SymDense{ + &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) { for cas, test := range []*SymDense{ NewSymDense(1, []float64{1}), diff --git a/mat/triangular.go b/mat/triangular.go index cd696286..a98cf3a3 100644 --- a/mat/triangular.go +++ b/mat/triangular.go @@ -230,6 +230,19 @@ func (t *TriDense) Reset() { t.mat.Data = t.mat.Data[:0] } +// Zero sets all of the matrix elements to zero. +func (t *TriDense) Zero() { + if t.isUpper() { + for i := 0; i < t.mat.N; i++ { + zero(t.mat.Data[i*t.mat.Stride+i : i*t.mat.Stride+t.mat.N]) + } + return + } + for i := 0; i < t.mat.N; i++ { + zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+i+1]) + } +} + // IsZero returns whether the receiver is zero-sized. Zero-sized matrices can be the // receiver for size-restricted operations. TriDense matrices can be zeroed using Reset. func (t *TriDense) IsZero() bool { diff --git a/mat/triangular_test.go b/mat/triangular_test.go index 08042146..4bd09c17 100644 --- a/mat/triangular_test.go +++ b/mat/triangular_test.go @@ -139,6 +139,51 @@ func TestTriAtSet(t *testing.T) { } } +func TestTriDenseZero(t *testing.T) { + // Elements that equal 1 should be set to zero, elements that equal -1 + // should remain unchanged. + for _, test := range []*TriDense{ + &TriDense{ + mat: blas64.Triangular{ + 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, + }, + }, + }, + &TriDense{ + mat: blas64.Triangular{ + Uplo: blas.Lower, + 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 TestTriDiagView(t *testing.T) { for cas, test := range []*TriDense{ NewTriDense(1, Upper, []float64{1}), diff --git a/mat/triband.go b/mat/triband.go index 8599620d..71ad38ff 100644 --- a/mat/triband.go +++ b/mat/triband.go @@ -256,6 +256,21 @@ func (t *TriBandDense) Reset() { t.mat.Data = t.mat.Data[:0] } +// Zero sets all of the matrix elements to zero. +func (t *TriBandDense) Zero() { + if t.isUpper() { + for i := 0; i < t.mat.N; i++ { + u := min(1+t.mat.K, t.mat.N-i) + zero(t.mat.Data[i*t.mat.Stride : i*t.mat.Stride+u]) + } + return + } + for i := 0; i < t.mat.N; i++ { + l := max(0, t.mat.K-i) + zero(t.mat.Data[i*t.mat.Stride+l : i*t.mat.Stride+t.mat.K+1]) + } +} + func (t *TriBandDense) isUpper() bool { return isUpperUplo(t.mat.Uplo) } diff --git a/mat/triband_test.go b/mat/triband_test.go index 93cd0d96..7571e38e 100644 --- a/mat/triband_test.go +++ b/mat/triband_test.go @@ -333,6 +333,57 @@ func TestTriBandAtSetUpper(t *testing.T) { } } +func TestTriBandDenseZero(t *testing.T) { + // Elements that equal 1 should be set to zero, elements that equal -1 + // should remain unchanged. + for _, test := range []*TriBandDense{ + &TriBandDense{ + mat: blas64.TriangularBand{ + Uplo: blas.Upper, + N: 6, + K: 2, + Stride: 5, + Data: []float64{ + 1, 1, 1, -1, -1, + 1, 1, 1, -1, -1, + 1, 1, 1, -1, -1, + 1, 1, 1, -1, -1, + 1, 1, -1, -1, -1, + 1, -1, -1, -1, -1, + }, + }, + }, + &TriBandDense{ + mat: blas64.TriangularBand{ + Uplo: blas.Lower, + N: 6, + K: 2, + Stride: 5, + Data: []float64{ + -1, -1, 1, -1, -1, + -1, 1, 1, -1, -1, + 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 TestTriBandDiagView(t *testing.T) { for cas, test := range []*TriBandDense{ NewTriBandDense(1, 0, Upper, []float64{1}), diff --git a/mat/vector.go b/mat/vector.go index 29b0ef35..8191312b 100644 --- a/mat/vector.go +++ b/mat/vector.go @@ -178,6 +178,13 @@ func (v *VecDense) Reset() { v.mat.Data = v.mat.Data[:0] } +// Zero sets all of the matrix elements to zero. +func (v *VecDense) Zero() { + for i := 0; i < v.mat.N; i++ { + v.mat.Data[v.mat.Inc*i] = 0 + } +} + // CloneVec makes a copy of a into the receiver, overwriting the previous value // of the receiver. func (v *VecDense) CloneVec(a Vector) { diff --git a/mat/vector_test.go b/mat/vector_test.go index ee6ff302..1be8313a 100644 --- a/mat/vector_test.go +++ b/mat/vector_test.go @@ -181,6 +181,38 @@ func TestVecDenseAtSet(t *testing.T) { } } +func TestVecDenseZero(t *testing.T) { + // Elements that equal 1 should be set to zero, elements that equal -1 + // should remain unchanged. + for _, test := range []*VecDense{ + &VecDense{ + mat: blas64.Vector{ + N: 5, + Inc: 2, + Data: []float64{ + 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 TestVecDenseMul(t *testing.T) { method := func(receiver, a, b Matrix) { type mulVecer interface {