Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mat: add basis for band matrix type #95

Merged
merged 3 commits into from Jul 8, 2017
Merged

mat: add basis for band matrix type #95

merged 3 commits into from Jul 8, 2017

Conversation

kortschak
Copy link
Member

This adds a starting point for building. I'm not currently happy with matrix construction since it's possible as the code stands to change the test case from 6x6 to 6x4 and no other changes without failure. I don't see a way to address this.

@btracey Please take a look.

mat/band.go Outdated
// data is used as the backing slice, and changes to the elements of the returned BandDense
// will be reflected in data. If neither of these is true, NewBandDense will panic.
//
// The data must be arranged in row-major order. Only the values in the band portion
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs a definition of "row-major order", as if you haven't seen banded matrices it's not obvious what the layout is. Something like a copy and paste from the BLAS documentation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

mat/band.go Outdated
mat blas64.Band
}

type Band interface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want the interface to be Banded rather than Band to match Triangular and Symmetric. I'm not sure if we want the type to be BandedDense instead of BandDense

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

mat/band_test.go Outdated
}

func TestBandAtSet(t *testing.T) {
band := NewBandDense(6, 6, 1, 2, []float64{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have a test that just accesses all of the matrix elements to make sure the index tests are correct.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@btracey
Copy link
Member

btracey commented Jun 23, 2017

I also do not see a way to address the allocation problem.

Two general concerns moving forward (not a problem with this PR).

  1. Do we have / could we have a way of maintaining a list of the special cases that we have implemented? It would be nice to have some big table where we could see gaps that need to be filled.

  2. Here is a nice property of banded matrices. The Cholesky decomposition of a (symmetric) banded matrix is also banded. It would be great if the Cholesky type could deal with that and "just work". In particular, somehow making, say, distmv.Normal agnostic to the representation. I believe the constraint on that particular issue is having the "Lower" extraction be agnostic.

@kortschak
Copy link
Member Author

PTAL

// TransposeBand is a type for performing an implicit transpose of a band
// matrix. It implements the Banded interface, returning values from the
// transpose of the matrix within.
type TransposeBand struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be TransposeBanded? In general, from a quick look, I don't understand the rules when Band or Banded is used.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did write it like that, but reverted because of length. Various people call these matrices band or banded. Here Banded is the interface and Band is the theoretical notion. I don't really mind either way.

btracey
btracey previously approved these changes Jul 6, 2017
mat/band.go Outdated
Matrix
// Triangular returns the number of rows/columns in the matrix and its
// orientation.
Bandwidth() (kl, ku int)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be plural?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The band's width is kl+ku+1, so no, I don't think so.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the doc comment should not say Triangular!

mat/band.go Outdated

// TBanded is the equivalent of the T() method in the Matrix interface but
// guarantees the transpose is of banded type.
TBanded() Banded
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be TBand to match TTri and the rest of the "abbreviation" conventions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if cols != test.c {
t.Errorf("unexpected number of cols for test %d: got: %d want: %d", i, cols, test.c)
}
if !reflect.DeepEqual(band, test.mat) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test here with the *Dense matrix and also use mat.Equal

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which *Dense matrix?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would write out the banded matrix as a *Dense with the zeros, and then test that equality.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping for

matDense: NewDense(6, 6, []float64{
1,  2,  3,  0,  0,  0,
4,  5,  6,  7,  0,  0,
0,  8,  9, 10, 11,  0,
0,  0, 12, 13, 14, 15,
0,  0,  0, 16, 17, 18,
0,  0,  0,  0, 19, 20,
})

and then comparing that with test.band

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, OK. Done.

}
}

// bandImplicit is an implicit band matrix returning val(i, j)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add these types to list_test as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I leave that for you? That system scares me a little.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me too. Sure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@kortschak
Copy link
Member Author

I will lose approval with these changes, so I'll rebase on to master while I'm here.

@kortschak
Copy link
Member Author

PTAL

@btracey
Copy link
Member

btracey commented Jul 6, 2017 via email

@kortschak kortschak force-pushed the banded branch 2 times, most recently from 0c50f60 to 5872b1e Compare July 6, 2017 05:04
@kortschak
Copy link
Member Author

OK, that and another missed typo are fixed. PTAL

btracey
btracey previously approved these changes Jul 6, 2017
@kortschak
Copy link
Member Author

kortschak commented Jul 6, 2017 via email

@kortschak
Copy link
Member Author

PTAL

btracey
btracey previously approved these changes Jul 6, 2017
mat/band.go Outdated
// 12 13 14 15
// 16 17 18 *
// 19 20 * *
// which is given to the passed to NewBandDense as []float64{*, 1, 2, 3, 4, ...}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which is passed to NewBandDense ... ?
Also, wouldn't it be worth to write that in this example kl == 1 and ku == 2?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

mat/band.go Outdated
return TransposeBand{b}
}

// RawMatrix returns the underlying blas64.Band used by the receiver.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RawBand

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

mat/band.go Outdated
return b.mat.Rows, b.mat.Cols
}

// Dims returns the upper and lower bandwidths of the matrix.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Bandwidth returns the lower and upper bandwidths ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

mat/band_test.go Outdated
}
}
}
// Do that same thing via a vall to Equal.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

via a call

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@kortschak
Copy link
Member Author

PTAL

@kortschak
Copy link
Member Author

ping @btracey since you are up.

// a new slice is allocated for the backing slice. If len(data) == min(r, c+kl)*(kl+ku+1),
// data is used as the backing slice, and changes to the elements of the returned
// BandDense will be reflected in data. If neither of these is true, NewBandDense
// will panic. kl must be at least zero and less r, and ku must be at least zero and
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note comments on kl and ku now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this behavior match Lapack? I think we want to be compliant here. I've only started looking at lapack banded implementations, but it would be nice to not have to play too many games (that said, we are stricter for Vector and it's worked out fine)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation says that the * elements are not referenced. If this causes a failure, I think then that the LAPACK implementation is buggy.

Looking at how we do this, I suspect it will fail for the gonum implementation, for example here. However, I think that is a result of our slice optimisations; the Fortran should not fail.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I agree. We/I should probably revisit the banded BLAS implementations before we're 6 layers deep in Lapack trying to figure out what's going on.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two ways to deal with those, either continue if the proposed length is 0, or probably preferably, make the same assessment that I am doing here.

mat/band.go Outdated
@@ -136,6 +136,12 @@ func NewBandDense(r, c, kl, ku int, data []float64) *BandDense {
}
}

// NewDiagonal is a convenience function that returns a diagonal matrix represented by a
// BandDense. The length of data must be min(r, c) otherwise NewDiagonal will panic.
func NewDiagonal(r, c int, data []float64) *BandDense {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please leave this out of this pull request.

I think we want a SymBanded interface / type. It makes a huge difference in distmv.NewNormal (O(n^3) to O(n) for computing the Cholesky), and is probably a common case in the spatial statistics world also. Typically, diagonal matrices are also square, so I think we would want this to return a SymBandDense by default.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sigma in SVD is potentially non-square. Similarly in GSVD. NewDiagonalBand and NewDiagonalSym? The reason this is here is purely for legibility to draw attention to the fact that the result is a diagonal.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree with the intent. Wikipedia suggests that the term Diagonal "usually refers to square matrices", and calls non-square ones "rectangular diagonal matrices".

I would go with NewDiagonal for the symmetric case, and NewDiagonalBand or NewDiagonalRect for the rectangular case.

data should be allowed to be nil, just like the other NewXxx functions. I commonly create a new matrix then use a loop to set the diagonal elements (and fortunately these will be much more efficient soon).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm an idiot. It can already be nil.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative is to have NewDiagonal(r, c int, data []float64) Matrix and return either a *BandDense or a *SymBandDense depending on whether r == c.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather have the two functions that return their specific types and not force type assertions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also have NewDiagSq and NewDiagRect

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll rename this to NewDiagonalRect.

@kortschak kortschak merged commit c08ba23 into master Jul 8, 2017
@kortschak kortschak deleted the banded branch July 8, 2017 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants