8000 Handling ndarrays with singular matrices in linalg functions · Issue #6027 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

Handling ndarrays with singular matrices in linalg functions #6027

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

Open
richardotis opened this issue Jun 30, 2015 · 5 comments
Open

Handling ndarrays with singular matrices in linalg functions #6027

richardotis opened this issue Jun 30, 2015 · 5 comments

Comments

@richardotis
Copy link

I am trying to solve several independent systems of equations at the same time using numpy.linalg.solve, e.g., a has shape (N, M, M). The challenge I'm running into is how to deal with the case when, for some values of N, the last two dimensions comprise a singular matrix.

Consider this example

import numpy as np
arr = [[[1, 0], [0, 1]], [[1, 0], [0, 1]], [[0, 0], [0, 0]]]
np.linalg.solve(arr, 1)

On numpy 1.9.2, this will raise a LinAlgError, but I still would like to know the solutions for all the full-rank matrices. (I would be okay with getting back NaNs for the singular cases.) I'm aware I can compute the singular values and use fancy indexing to slice the array, but in my algorithm I have to do several slicing/filtering steps and I would prefer not to lose the alignment of a with b -- my real-world a typically has 6-7 dimensions and all the indexing arrays make the code hard to follow.

My ideal solution seems to involve masked arrays since I end up doing several other filtering steps, but it appears that most (all?) functions in the linalg family ignore array masks.

Is there an alternative solution already available, or perhaps a suggestion on how I could contribute a solution that I could package as a PR?

@njsmith
Copy link
Member
njsmith commented Jun 30, 2015

I don't know of any really good workarounds, no.

There was some discussion of this issue when we first merged the ability to solve stacks of equations in a single call (which happened in #3220, a follow-up to #2954, and see also #3217). Basically, we all agreed that the LinAlgError behaviour is suboptimal for the reasons you suggest (inability to get out partial results), but that getting this right required some proper thought, and we didn't want to try and make up some hack in a few minutes while in the middle of getting the basic infrastructure in place. So we decided to stick with the crude LinAlgError approach at first, defer a proper fix until someone found time and attention to do it properly.

I posted some general thoughts on how this might work, and the questions we'd need to answer, here.

If I were going to spitball a solution off the top of my head, it'd be something like:

  • We add a new thread-local storage that stores "error flags" (in the sense of np.seterr); each error type gets assigned a single flag bit, and the ufunc code is adapted to add a way to set these flag bits and also check for them at the end of a computation. The idea here is that, right now, the only way for a ufunc loop to communicate an error condition to the outside world is through the floating point error flags that are built into the CPU. (This is how np.seterr etc. work right now.) This is awkward because it means that we are restricted to the IEEE754 error flags, and also because explicitly setting/clearing these flags requires cumbersome (and sometimes slow) FPU code. Of course we would still have to check the FPU flags in general, but having a second set of flags that we control makes things much more flexible.
  • Take advantage of this flexibility by adding a new linalg error type to np.seterr
  • Teach the functions in np.linalg to set this flag instead of directly raising an exception.

So the idea would be that by default we would continue to error (or at least warn) when singular matrices were passed to solve (i.e., the default setting for the linalg error handler would be error or warn), but for your code where you are expecting singular matrices and are prepared for them, you could write:

with np.errstate(linalg="ignore"):
    np.linalg.solve(stack_with_singular_matrices)

and silently get back matrices full of NaNs when appropriate.

That's just one idea, though; any real proposal would need a bit of thought into the questions posted in that link above, and to then get posted on the mailing list for wider review to make sure we didn't accidentally miss some important issue.

@ovillellas
Copy link

As far as I remember, the underlying gufunc used to implement solve does report any error in the underlying call to LAPACK by setting the results to NaNs. The linalg module function provides an "extobj" so that if that condition happens the exception is raised.

Maybe using seterr to 'ignore' for invalid will prevent the exception from being raised, with invalid entries having its results set to NaN. It could also be possible to call the underlying gufunc without extobj (and using the appropriate seterr as well).

Maybe adding a keyword to the linalg wrappers so that they don't raise exceptions and just return the results with NaNs for invalid entries would be useful (this could be implemented in all the linalg functions that are 'gufunc-like' and that can raise exceptions).

Another option would be to have the results available even if an exception is raised (maybe in the exception object). This would allow the user the following 3 options:

  • Ignore the exception by recovering the results with NaNs in invalid entries, maybe applying corrective code to the results.
  • Handle like an error in a way that is compatible with current code.
  • Use different codepaths for "all-correct" and "some-failed" results.

@pv
Copy link
Member
pv commented Jun 30, 2015 via email

@ovillellas
Copy link

In fact, when implementing the gufuncs, error handling was a bit of a caveat, but
I went the NaN-way as it was enough for my purposes (another caveat was not being
able to 'compute' output dimensions).

IMHO, a plain exception raised when any of the elements has an error condition is
not appropriate without extra information. At least it should be possible to have access
to the elements that didn't have problems when computing them, as well as knowing
which elements had errors -and ideally which errors-. That would require some tweaks
in the ufunc machinery to support error conditions. Another option would be to have an
extra output argument with per-element error codes that could be handled by the wrapper
function.

Note that implementing a "noexcept" keyword would just be a matter of not providing and extobj to
the underlying ufunc. That will provide the "errors as NaNs" semantics, respecting the seterr settings.
As long as the default is False, the old behaviour would be met.

@pv
Copy link
Member
pv commented Jun 30, 2015 via email

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

No branches or pull requests

4 participants
0