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
Conversation
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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{ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
I also do not see a way to address the allocation problem. Two general concerns moving forward (not a problem with this PR).
|
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
mat/band.go
Outdated
Matrix | ||
// Triangular returns the number of rows/columns in the matrix and its | ||
// orientation. | ||
Bandwidth() (kl, ku int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be plural?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which *Dense
matrix?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, I think.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Me too. Sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
I will lose approval with these changes, so I'll rebase on to master while I'm here. |
PTAL |
It should also probably actually say what the function does.
Sorry for missing that.
… On Jul 5, 2017, at 11:01 PM, Dan Kortschak ***@***.***> wrote:
@kortschak commented on this pull request.
In mat/band.go <#95 (comment)>:
> + bandDense *BandDense
+ _ Matrix = bandDense
+ _ Banded = bandDense
+ _ RawBand = bandDense
+)
+
+// BandDense represents a band matrix in dense storage format.
+type BandDense struct {
+ mat blas64.Band
+}
+
+type Banded interface {
+ Matrix
+ // Triangular returns the number of rows/columns in the matrix and its
+ // orientation.
+ Bandwidth() (kl, ku int)
But the doc comment should not say Triangular!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#95 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/ADgqWxl9RYjs2UJULoi_wib5qesIcXbSks5sLGoRgaJpZM4OAbXx>.
|
0c50f60
to
5872b1e
Compare
OK, that and another missed typo are fixed. PTAL |
I'll make those changes later today. This will invalidate the approval.
|
PTAL |
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, ...}. |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RawBand
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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 ...
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
via a call
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
PTAL |
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
.
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.