Repository navigation
BUG: bvls can fail catastrophically to converge - #12709
Conversation
The function `bvls`, which is called by `optimize.lsq_linear(... method='bvls')` can fail catastrophically to converge due to an incorrect implementation of the published BVLS algorithm. It is supposed to implement the BVLS algorithm by [Stak & Parker 1995](https://www.stat.berkeley.edu/~stark/Preprints/bvls.pdf), which is a small generalization of the classic NNLS algorithm 23.10 by [Lawson & Hanson (1995)](https://doi.org/10.1137/1.9781611971217). However, the current Scipy implementation fails to implement the so-called loop-B in the notation of the above two papers. The proposed changes fix the deficiency and allow the algorithm to converge in the cases where it currently fails.
tylerjereddy
left a comment
There was a problem hiding this comment.
Would it be practical to add a regression test based on the minimal example provided?
|
|
||
|
|
||
| def bvls(A, b, x_lsq, lb, ub, tol, max_iter, verbose): | ||
| def bvls(A, b, x_lsq, lb, ub, tol, max_iter, verbose, rcond=None): |
There was a problem hiding this comment.
Is rcond parameter significant for this algorithm? If so we should pass it through lsq_linear.
Can we get away with default rcond=-1 which uses machine epsilon to cutoff small singular values? I wouldn't introduce another parameter if there are no cases for it.
There was a problem hiding this comment.
The usefulness of rcond in bvls is the same as for linalg.lstsq. In most cases the default is enough, but the extra generality can be useful in some instances, as the default test for rank deficiency is too restrictive e.g. when the matrix contain measurement errors in addition to just numerical errors.
|
I think this algorithm was implemented based only on the BVLS paper. They don't go into details on implementation and only mention "Loop B" briefly. As far as I understand the problem arises because of finite precision computations, but of course it is important for numerical methods.
I think at least one test to trigger this problem is a must. And, if possible, I would suggest extending the tests for this algorithm significantly as it proved to be fragile. Adding a test with "with more realistic larger arrays" would be very suitable. |
|
Although "loop B" is briefly mentioned, the algorithm in Sec. 2 of Stark & Parker (1995), which closely follows Lawson & Hanson, states that one should go to step 2 (exit loop B) if the Indeed dealing with numerical errors are critical here. The typical problem is that one may select the wrong variable to be added to the free set and, when using a test on the relative cost decrease, the algorithm may incorrectly conclude that no decrease is possible and terminate. The Lawson & Hanson and Stark & Parker algorithms avoid having a simple test of convergence based on the relative cost decrease between iterations, to avoid premature convergence. Instead, they rely on the KKT test only. However, this requires using slightly more complex techniques to avoid cycling of the active set (discussed in the papers), which can cause endless loops. In my experience, the current fixed implementation seems robust enough. NOTE: The need for loop B is because, just after the linear interpolation of the solution, in general, the solution will not be a minimum of the quadratic function for the given active set. For this reason, the test of convergence in loop A will be invalid: the procedure should never stop just after interpolating the solution. Iterating within Loop B makes sure that the solution is a minimum of the quadratic function for the given active set before exiting the loop and testing for convergence in loop A. |
|
Here is a more realistic example with large rank-deficient matrices which gives before the fix and after the fix |
|
@divenex it all sounds very good. Will you add 2 tests on which current implementation fails? As for |
|
@nmayorov where/how would I add the tests? |
|
This is the file with tests https://github.com/scipy/scipy/blob/master/scipy/optimize/tests/test_lsq_linear.py Add tests into |
|
Any problems with adding tests from #12816 as another commit here? |
(cherry picked from commit 1d28c7a)
I blindly cherry-picked those tests, they had some syntax issues. Fixed those and made the linter happy too, but the tests are failing - this needs work it looks like. |
|
It was a trivial syntax error. Now tests are active and passing. I think we should merge it finally. |
The function
bvls, which is called byoptimize.lsq_linear(... method='bvls')can fail catastrophically to converge due to an incorrect implementation of the published BVLS algorithm.It is supposed to implement the BVLS algorithm by Stak & Parker 1995, which is a small generalization of the classic NNLS algorithm 23.10 by Lawson & Hanson (1995).
However, the current Scipy implementation fails to implement the so-called loop-B in the notation of the above two papers.
The proposed changes fix the deficiency and allow the algorithm to converge in the cases where it currently fails.
Reference issue
Here is a minimal example where the current implementation fails
And here is the corresponding output of Scipy 1.5
Additional information
The current implementation fails in various other examples, with more realistic larger arrays, and especially when the matrix A is rank deficient. However, I selected a very simple example to make it easy to understand what the code is doing differently in the proposed changes.