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
Implement simple macro for matrix creation #47
Conversation
I haven't written any documentation. If you want the macro in the library, where do you want me to write the documentation for it? |
Thanks for working on this! It actually already exists but isn't exposed. The implementation is very similar to yours on the surface but the underlying macros are different. I wrote the existing one quite a while ago (my first ever macro) so yours may be significantly better! I would like to see this available for users! I think the best approach to take would be to move your implementation and tests into the existing |
Oh, I didn't realize! Yeah, this was also the first time I touched Rust's macro system. Yours seems to be significantly more advanced than mine, although from your own comment in |
I don't really remember the motivation behind much of the complication in mine - because of this we should probably go with yours. Implementing the extensions I describe in that module are probably impossible? I'm not too fussed about those right now. I think the macros are most valuable for small matrices as you say. For the documentation I think we could include an example in the crate documentation. And then just document the macro itself well - like vec!. |
@AtheMathmo: I made the requested changes. Was this what you had in mind...? |
/// heap. | ||
/// | ||
/// Rows are separated by semi-colons, while commas separate the columns. | ||
/// Users of MATLAB will find this style familiar. If the dimensions |
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 believe the macro will not fail if the dimensions do not match?
In fact - it is possible to create a matrix that doesn't match the expected dimensions?
// Will think cols = 2, rows = 3 - 6 elements
matrix!(1.0, 1.0; 2.0, 2.0, 2.0; 3.0)
I'm not super fussed about this fail case - though I think it might be why I had the more complex setup before - to count the elements in each row and ensure they were equal (it looks incomplete?)
If I am misunderstanding and it does indeed fail - we should totally add a test for this as it is a behaviour I would love to keep!
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 does actually fail at compile time. I made a quick hack by pasting your example into the matrix_macro()
test:
{
// Will think cols = 2, rows = 3 - 6 elements
let mat = matrix!(1.0, 1.0; 2.0, 2.0, 2.0; 3.0);
}
and I get the error:
src/macros.rs:51:45: 51:56 error: mismatched types [E0308]
src/macros.rs:51 let data_as_nested_array = [ $( [ $($x),* ] ),* ];
^~~~~~~~~~~
src/macros.rs:107:23: 107:60 note: in this expansion of matrix! (defined in src/macros.rs)
src/macros.rs:51:45: 51:56 help: run `rustc --explain E0308` to see a detailed explanation
src/macros.rs:51:45: 51:56 note: expected type `[_; 2]`
src/macros.rs:51:45: 51:56 note: found type `[_; 3]`
src/macros.rs:51:45: 51:56 error: mismatched types [E0308]
src/macros.rs:51 let data_as_nested_array = [ $( [ $($x),* ] ),* ];
^~~~~~~~~~~
src/macros.rs:107:23: 107:60 note: in this expansion of matrix! (defined in src/macros.rs)
src/macros.rs:51:45: 51:56 help: run `rustc --explain E0308` to see a detailed explanation
src/macros.rs:51:45: 51:56 note: expected type `[_; 2]`
src/macros.rs:51:45: 51:56 note: found type `[_; 1]`
error: aborting due to 2 previous errors
error: Could not compile `rulinalg`.
To learn more, run the command again with --verbose.
The macro basically converts the expression into a nested array (i.e. for 3x2 it will construct [ [ T; 2]; 3 ]
) and relies on the fact that nested arrays can not have variable sizes. As far as I can tell, it is indeed impossible to construct a matrix whose dimensions are not compatible.
I didn't add a test for that, because I had no idea how to add a test for breaking the compilation. I can go ahead and add the compiletest-rs
crate, if you want!
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 that the error is not particularly readable, but I don't think there's anything to do about that. However, if you read it closely, it does actually say that the dimensions are wrong, in the sense of arrays.
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 just having this fail at compile time is a big advantage! Even if the error message is a little hard to parse (I find that most macro errors are very hard to decipher).
I haven't really looked into the compiletest-rs crate. If you're happy to do so I think it is worthwhile. I'll trust you if you say it is worth including (note that it should be a devDependency
as in the crate README though).
Thanks!
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.
Sure, I can take a look (although not sure exactly when). Agree that it would be nice to be able to assert this behavior through tests!
The docs and everything look great. I have one comment which doesn't need any action. If it is an issue like I think, we can write this up and get it in as-is. If you are correct (I didn't actually test it...) - then I'd like to get a test to enforce this but this can wait too. |
@AtheMathmo: see my reply to the comment. I can add the compile-time test next chance I get to work on this. I think there's no particular hurry to get this in immediately, so we might as well get that test in proper first. |
That's fine - whenever you have time. I have one other question. Do you think it is worth emphasizing that this should be used for small matrices only? As we allocate the data on the stack first with the array and then copy it to the heap. Personally I think the documentation is fine right now and this should never really be a problem. |
I would hope that nobody in their right minds would populate a big matrix manually in their source code! That said, there may be the case where someone allocates small matrices in a tight loop... But then I think But even then, considering the fact that the stack allocation is likely more or less free compared to the heap copy, and the fact that a regular construction of Matrix with its constructor would involve a copy to the heap (possibly from static storage, or maybe it even uses stack storage too, I have no idea), I'd be surprised if there is any noticeable performance difference at all! Of course, we'd have to bench that to check, but I think it's not something we have to worry about. Just a small note: once this is in, perhaps we should try to make sure test data for new tests are written using the macro, because the compile-time checking might save us some future headache from accidental bugs such as mixing up Matrix::new(3, 2, ...) and Matrix::new(2, 3, ...) with the same data! |
Ok, great! We'll keep the docs as is and refer to your comment in the future if this ever arises :) .
I agree. I'll write up a ticket once this is merged to update existing tests where relevant too. Edit: Just adding this to remind myself. This is waiting on research into compile-fail tests for matrix construction. We can merge happily without these if the effort/impact of adding them is too great. |
OK, so I spent about 30 minutes looking into
That looks good, right? Well,
Note the line where it says
For whatever reason, I cannot get it to recognize any error at all. Also, note that I have to supply dependencies by The idea is great, but it looks to me that the added brittleness and complexity isn't quite worth it for us yet. Maybe once it matures. For completeness, here is the modified
|
By the way, I used the nightly compiler for the above. I'd have to say, the error message from the In any case, I suggest we merge this as it is for now. We should pay extra attention to any modifications done to |
Thanks for taking the time to look into this. It's great that the nightly errors are even clearer! I think you've made the right call. I'll merge this as it stands. |
Just leaving this here for future reference. I posted an issue to the |
* correct eigenmethods for 2-by-2 (and 1-by-1) matrices In issue AtheMathmo#46, it was observed that the `eigenvalue` and `eigendecomp` methods could give incorrect results. Rusty-machine creator and BDFL James Lucas noted that the problem was specific to 2-by-2 matrices and suggested special-casing them. * private methods for general and small matrix eigencomputations, test Conflicts: rusty-machine/src/linalg/matrix/decomposition.rs rusty-machine/src/linalg/matrix/mod.rs
There is no issue for this, but I've been missing a simple macro for creating small matrices. So I wrote a very simple one. It works like this:
As I'm sure you can infer from the example, you delimit columns by commas and rows by semi-colons, which is also the way MATLAB does it, except I chose to make the commas compulsory. Since I'm simply converting the expression to a Rust nested array, you get compiler errors if your matrix dimensions don't match, which is an advantage compared to constructor methods (which would also need you to specify rows and cols).
Such a macro is useful not just for ourselves when testing
rulinalg
, but for users ofrulinalg
who need to create small matrices when writing tests of their own applications.What do you think?