Skip to content

Commit

Permalink
mat: add Doer interfaces and implementations
Browse files Browse the repository at this point in the history
Also clean up some documentation and missing type checks related to
tests for Doers.
  • Loading branch information
kortschak committed Jul 8, 2017
1 parent a6d9d8f commit e594810
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 3 deletions.
44 changes: 44 additions & 0 deletions mat/band.go
Expand Up @@ -13,13 +13,16 @@ var (
_ Matrix = bandDense
_ Banded = bandDense
_ RawBand = bandDense

_ Doer = bandDense
)

// BandDense represents a band matrix in dense storage format.
type BandDense struct {
mat blas64.Band
}

// Banded represents a band matrix.
type Banded interface {
Matrix
// Bandwidth returns the lower and upper bandwidth values for
Expand All @@ -31,10 +34,17 @@ type Banded interface {
TBand() Banded
}

// A RawBand can return a view of itself as a BLAS Band matrix.
type RawBand interface {
RawBand() blas64.Band
}

// A MutableBanded can set elements of a band matrix.
type MutableBanded interface {
Banded
SetBand(i, j int, v float64)
}

var (
_ Matrix = TransposeBand{}
_ Banded = TransposeBand{}
Expand Down Expand Up @@ -168,3 +178,37 @@ func (b *BandDense) TBand() Banded {
func (b *BandDense) RawBand() blas64.Band {
return b.mat
}

// Do calls the function fn for each of the filled elements of b. The function fn
// takes a row/column index and the element value of b at (i, j).
func (b *BandDense) Do(fn func(i, j int, v float64)) {
for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ {
for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ {
fn(i, j, b.at(i, j))
}
}
}

// DoRow calls the function fn for each of the filled elements of row i of b. The function fn
// takes a row/column index and the element value of b at (i, j).
func (b *BandDense) DoRow(i int, fn func(i, j int, v float64)) {
if i < 0 || b.mat.Rows <= i {
panic(ErrRowAccess)
}
for j := max(0, i-b.mat.KL); j < min(b.mat.Cols, i+b.mat.KU+1); j++ {
fn(i, j, b.at(i, j))
}
}

// DoCol calls the function fn for each of the filled elements of column j of b. The function fn
// takes a row/column index and the element value of b at (i, j).
func (b *BandDense) DoCol(j int, fn func(i, j int, v float64)) {
if j < 0 || b.mat.Cols <= j {
panic(ErrColAccess)
}
for i := 0; i < min(b.mat.Rows, b.mat.Cols+b.mat.KL); i++ {
if i-b.mat.KL <= j && j < i+b.mat.KU+1 {
fn(i, j, b.at(i, j))
}
}
}
18 changes: 18 additions & 0 deletions mat/matrix.go
Expand Up @@ -182,6 +182,24 @@ type RawVectorer interface {
RawVector() blas64.Vector
}

// A Doer can call a function for each filled element of the receiver. The parameters
// of the function are the element indices and its value.
type Doer interface {
Do(func(i, j int, v float64))
}

// A RowDoer can call a function for each filled element of a row of the receiver.
// The parameters of the function are the element indices and its value.
type RowDoer interface {
DoRow(i int, fn func(i, j int, v float64))
}

// A ColDoer can call a function for each filled element of a column of the receiver.
// The parameters of the function are the element indices and its value.
type ColDoer interface {
DoCol(j int, fn func(i, j int, v float64))
}

// TODO(btracey): Consider adding CopyCol/CopyRow if the behavior seems useful.
// TODO(btracey): Add in fast paths to Row/Col for the other concrete types
// (TriDense, etc.) as well as relevant interfaces (RowColer, RawRowViewer, etc.)
Expand Down
77 changes: 77 additions & 0 deletions mat/matrix_test.go
Expand Up @@ -521,3 +521,80 @@ func TestTrace(t *testing.T) {
}
testOneInputFunc(t, "Trace", f, denseComparison, sameAnswerFloat, isAnyType, isSquare)
}

func TestDoer(t *testing.T) {
type MatrixDoer interface {
Matrix
Doer
RowDoer
ColDoer
}
ones := func(n int) []float64 {
data := make([]float64, n)
for i := range data {
data[i] = 1
}
return data
}
for i, m := range []MatrixDoer{
//NewTriDense(3, Lower, ones(3*3)),
//NewTriDense(3, Upper, ones(3*3)),
//NewBandDense(6, 6, 1, 1, ones(3*6)),
NewBandDense(6, 10, 1, 1, ones(3*6)),
//NewBandDense(10, 6, 1, 1, ones(7*3)),
} {
r, c := m.Dims()

want := Sum(m)

var got float64
fn := func(i, j int, v float64) {
got += v
switch m := m.(type) {
case MutableTriangular:
m.SetTri(i, j, v)
case MutableBanded:
m.SetBand(i, j, v)
default:
panic("bad test: need mutable type")
}
}

panicked, message := panics(func() { m.Do(fn) })
if panicked {
t.Errorf("unexpected panic for Doer test %d: %q", i, message)
continue
}
if got != want {
t.Errorf("unexpected Doer sum: got:%f want:%f", got, want)
}

got = 0
panicked, message = panics(func() {
for i := 0; i < r; i++ {
m.DoRow(i, fn)
}
})
if panicked {
t.Errorf("unexpected panic for RowDoer test %d: %q", i, message)
continue
}
if got != want {
t.Errorf("unexpected RowDoer sum: got:%f want:%f", got, want)
}

got = 0
panicked, message = panics(func() {
for j := 0; j < c; j++ {
m.DoCol(j, fn)
}
})
if panicked {
t.Errorf("unexpected panic for ColDoer test %d: %q", i, message)
continue
}
if got != want {
t.Errorf("unexpected ColDoer sum: got:%f want:%f", got, want)
}
}
}
1 change: 1 addition & 0 deletions mat/symmetric.go
Expand Up @@ -45,6 +45,7 @@ type RawSymmetricer interface {
RawSymmetric() blas64.Symmetric
}

// A MutableSymmetric can set elements of a symmetric matrix.
type MutableSymmetric interface {
Symmetric
SetSym(i, j int, v float64)
Expand Down
69 changes: 66 additions & 3 deletions mat/triangular.go
Expand Up @@ -14,9 +14,12 @@ import (

var (
triDense *TriDense
_ Matrix = triDense
_ Triangular = triDense
_ RawTriangular = triDense
_ Matrix = triDense
_ Triangular = triDense
_ RawTriangular = triDense
_ MutableTriangular = triDense

_ Doer = triDense
)

const badTriCap = "mat: bad capacity for TriDense"
Expand All @@ -28,6 +31,7 @@ type TriDense struct {
cap int
}

// Triangular represents a triangular matrix. Triangular matrices are always square.
type Triangular interface {
Matrix
// Triangular returns the number of rows/columns in the matrix and its
Expand All @@ -39,10 +43,17 @@ type Triangular interface {
TTri() Triangular
}

// A RawTriangular can return a view of itself as a BLAS Triangular matrix.
type RawTriangular interface {
RawTriangular() blas64.Triangular
}

// A MutableTriangular can set elements of a triangular matrix.
type MutableTriangular interface {
Triangular
SetTri(i, j int, v float64)
}

var (
_ Matrix = TransposeTri{}
_ Triangular = TransposeTri{}
Expand Down Expand Up @@ -455,3 +466,55 @@ func copySymIntoTriangle(t *TriDense, s Symmetric) {
}
}
}

// Do calls the function fn for each of the filled elements of t. The function fn
// takes a row/column index and the element value of t at (i, j).
func (t *TriDense) Do(fn func(i, j int, v float64)) {
if t.isUpper() {
for i := 0; i < t.mat.N; i++ {
for j := i; j < t.mat.N; j++ {
fn(i, j, t.at(i, j))
}
}
return
}
for i := 0; i < t.mat.N; i++ {
for j := 0; j <= i; j++ {
fn(i, j, t.at(i, j))
}
}
}

// DoRow calls the function fn for each of the filled elements of row i of t. The function fn
// takes a row/column index and the element value of t at (i, j).
func (t *TriDense) DoRow(i int, fn func(i, j int, v float64)) {
if i < 0 || t.mat.N <= i {
panic(ErrRowAccess)
}
if t.isUpper() {
for j := i; j < t.mat.N; j++ {
fn(i, j, t.at(i, j))
}
return
}
for j := 0; j <= i; j++ {
fn(i, j, t.at(i, j))
}
}

// DoCol calls the function fn for each of the filled elements of column j of t. The function fn
// takes a row/column index and the element value of t at (i, j).
func (t *TriDense) DoCol(j int, fn func(i, j int, v float64)) {
if j < 0 || t.mat.N <= j {
panic(ErrColAccess)
}
if t.isUpper() {
for i := 0; i <= j; i++ {
fn(i, j, t.at(i, j))
}
return
}
for i := j; i < t.mat.N; i++ {
fn(i, j, t.at(i, j))
}
}

0 comments on commit e594810

Please sign in to comment.