Open tab58 opened 8 years ago
Added a couple notes. Overall LGTM. I support robustness first, followed by optimization.
One additional question: do you think we should throw errors if the dimensionality or dimension sizes are incompatible? I don't think we have so far, but I think it's the right move. The general conclusion before was to return false if the algorithm fails and throw an error if the operation is invalid. The actual amount of work involved is almost certainly negligible unless you're multiplying 2 x 2 matrices 10000000 times—which might not be that unrealistic but is probably a bad idea if it's actually the bottleneck.
K, before we hit the big green button, I realized my test for this is terrible. I was having some rebase issues in Git, so I had to do some manual file moving and force pushes to my fork. The tests aren't right for this. Let me fix this before we merge.
This should be fixed now. If this looks good to you @rreusser, hit the big green button. 😄
Could you add a note on the storage format first? Coming to this library, I'd be unable to use the sbmv
without either working through the code or locating another reference on the topic. We maybe don't need a whole code snippet, but we at least need to describe that the diagonal is in… row k
of A? With superdiagonals above that? I'm not sure if that's correct, but we should at least describe it to the point that it's usable.
Made a few comments. Hate to nitpick too much, but I think it's important to get the API and usage consistent and tight so that people don't have to worry about all the caveats and subtleties. Here's the things I'd like to tighten up in general, and I'm glad to help with the cleanup:
true
in general (there's no possibility of failure here, but makes it more meaningful to return false
for algorithms that can fail)If that makes sense, let's merge this since it's a big step forward, then continue with building it out before publishing to npm.
And despite all that, please don't think I'm not grateful and excited that you're back to work on this!
You're just fine with the nitpicks. It's good that you're asking questions cause that means you're analyzing my code. 😄 I would hate to commit something unusable. Let me address your comments:
sbmv
case, maybe fromLower
is somewhat redundant, given that A
is stored as a regular dense matrix. I think the real impetus behind this (for me anyway) was a case where the different triangular portions of the matrix mean different things. For example, there is a way to perform LU decomposition and calculate the L and U matrices in place, so maybe you want to operate on it with a method like this. I don't know why this case would ever come up, but I could imagine some other slick algorithm doing something similar. Basically it was to make sure we didn't deny any options to the user. Besides, fromLower
defaults to true if not supplied, so I thought I was safe. Maybe you have different thoughts on the matter.What are your feelings on using console.warn()
for reasons the function failed, @rreusser?
Hmm… based on past mistakes, let's avoid console.warn if we can. If you trap an error and choose to ignore it, you might still get tons of unwanted console output. Most people don't trap unexpected errors, so as long as you throw new Error('Useful message here')
, it should show up in the console anyway.
So we do want to throw errors instead of console.warn()
? That's cool. I'll do that.
But if we're throwing errors on argument mismatches, then why would we then want to return false?
I like console.warn
for things like a recursion limit reached where you might still get a meaningful answer but with caveats. And if you do that, I think the right way is to keep a module-global variable that counts how many times the error has been logged, and then omit further errors after some reasonable limit. Bottom line: except for that very specific case of true 'warnings' as opposed to errors or algorithm failures, it's a small bit of unpleasantness that I think is best avoided.
And as for errors, I think only throw errors on invalid input. So:
.get
wasn't a function on the number 7 because 7 is not an ndarray)As opposed to:
false
. This is distinct from invalid input since the input may have been perfectly valid and it's the algorithm that's not capable of proceeding.The downside being that it's difficult to handle multiple return values (e.g. a numerical result + true/false representing success/failure—which you can do but it's kinda overloading the meaning of the return value such that any additional details are getting to be a bit much for one return value). One approach I've taken with that is to accept an optional status
object as a final object. If provided, then additional information about the result is computed and passed back. See complex-deriv-fornberg for an example. (This could also be accomplished with a callback. I'm not sure how I feel about that.)
To answer your specific question though, this algorithm should never need to return false. A return true
at the end should be sufficient. Sorry, I was speaking in more general terms.
Added some things @rreusser. LMK if you have any issues with them.
Referenced LAPACK library for logic: http://www.netlib.org/lapack/explore-html/d7/d15/group__double__blas__level2_ga5c7ca036c788c5fd42e04ade0dc92d44.html#ga5c7ca036c788c5fd42e04ade0dc92d44