-
-
Notifications
You must be signed in to change notification settings - Fork 11k
BUG, API: np.random.multivariate_normal behavior with bad covariance matrix #5726
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
Conversation
Just as a technical comment, is there a reason to use |
The was a discussion for switching to eigh, or cholesky. eigh seems better. here but that seemed out of scope of this PR, but it is on my todo list. In the code there is a comment that says |
OK, sorry for rehashing the old discussion. |
Needs to be discussed on the list. Another option is to add a |
@charris I'll ping the list. On Travis-CI a This doesn't happen on my local machine, what is different on Travis-CI? |
Try |
Note that symettry and almost positive semidefinite can be checked with the svd. Something like
The tolerances for |
@charris It didn't generate much discussion on list (link), but I think @josef-pkt's comments were helpful. To summarize, instead of seperate kwargs for checking symmetry, and PSDness, just one kwarg |
@charris Rehashed this. Basically we now allow for small floating point errors in the covariance matrix. And I added a |
ping @josef-pkt |
8000
(u, s, v) = svd(cov) | ||
neg = (np.sum(u.T * v, axis=1) < 0) & (s > 0) | ||
|
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 be the entire next part under
if check_valid != 'ignore':
no reason to check if we 'ignore'
In np.random.multivariate_normal allow the covariance matrix to have small floating point errors. And allow control over what to do if the PSD check fails.
Test that warnings are or are not raised when check_valid kwarg is used.
☔ The latest upstream changes (presumably #7082) made this pull request unmergeable. Please resolve the merge conflicts. |
You managed to confuse GitHub, haven't seen that before:) Have you taken over this PR, and is it ready to merge? |
Yes, I just took over this PR. It should be ready to commit (it was just a matter of cleaning up a bit the test). |
Okay thanks. I'll review it now then, before we forget about it again. |
@@ -4380,6 +4381,10 @@ cdef class RandomState: | |||
generated, and packed in an `m`-by-`n`-by-`k` arrangement. Because | |||
each sample is `N`-dimensional, the output shape is ``(m,n,k,N)``. | |||
If no shape is specified, a single (`N`-D) sample is returned. | |||
check_valid : 'warn', 'raise', 'ignore' |
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.
formatting: change to {'warn', 'raise', 'ignore'}, optional
@@ -4380,6 +4381,10 @@ cdef class RandomState: | |||
generated, and packed in an `m`-by-`n`-by-`k` arrangement. Because | |||
each sample is `N`-dimensional, the output shape is ``(m,n,k,N)``. | |||
If no shape is specified, a single (`N`-D) sample is returned. | |||
check_valid : 'warn', 'raise', 'ignore' | |||
Behavior when the covariance matrix is not Positive Semi-definite. |
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.
style nitpick: use lower case for positive semidefinite
@@ -4380,6 +4381,10 @@ cdef class RandomState: | |||
generated, and packed in an `m`-by-`n`-by-`k` arrangement. Because | |||
each sample is `N`-dimensional, the output shape is ``(m,n,k,N)``. | |||
If no shape is specified, a single (`N`-D) sample is returned. | |||
check_valid : 'warn', 'raise', 'ignore' | |||
Behavior when the covariance matrix is not Positive Semi-definite. | |||
tol : float |
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.
float, optional
if mean.shape[0] != cov.shape[0]: | ||
raise ValueError("mean and cov must have same length") | ||
raise ValueError("mean and cov must have same length") | ||
|
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 you add a check for check_valid
being one of the 3 accepted strings?
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.
actually, move it from line 4511 up here. It's misplaced there; only checked for when not psd
.
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.
that looks good 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.
note that the check is itself later in the code, when checking. However, it may go unchecked when the psd passes, so I have rearranged the logic so it is always checked.
check_valid : 'warn', 'raise', 'ignore' | ||
Behavior when the covariance matrix is not Positive Semi-definite. | ||
tol : float | ||
Tolerance of the singular values in covariance 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.
absolute or relative tolerance?
RuntimeWarning) | ||
|
||
if check_valid != 'ignore': | ||
psd = np.allclose(np.dot(v.T * s, v), cov) |
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.
tol
should be used here. It's not used anywhere 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.
Can you add a test with non-default tol
for good measure?
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 realize. I wonder if tol should be changed to atol and rtol and just forward them in here.
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.
You'll have to specify both atol
and rtol
(here, not in the signature), because the defaults are both nonzero: rtol=1e-05, atol=1e-08
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 my comment wasn't a reply to yours, non-updating interface.
atol = rtol = tol
seems okay. atol
is the only one of interest near covariance values of 0, and rtol
may do something useful for very large values.
Overall LGTM, let's get this merged once comments are addressed. |
Ok, just pushed the documentation changes. Also:
updated the docstring accordingly |
I wonder if that's better. Realistically no one will use |
Ok, I will change it back to the single tol and pass it as atol and rtol |
Needs a mention in |
reverted to a single tol argument. I also added a mention in the release notes under "changes" |
Okay, in it goes! Thanks @ovillellas, @cowlicks, all. |
Edit:
This now allows for PSD checking with a
check_valid
keyword arg that can take the values'warn'
,'raise'
, and'ignore'
.It also allows the covariance matrix to be almost PSD up to some small errors.
There have been several old discussions about how
np.random.multivariate_normal
should behave when passed a "bad" covariance matrix. i.e. non-symmetric, or not Positive semi-definite (PSD). The reason people need this is because the covariance matrix might have roundoff error making it not strictly symmetric or PSD.In the discussions there was some consensus to fix things the way I do in this PR. I added a keyword arg for ignoring the PSD check and silencing the warning. I also added a keyword arg that ignores the new symmetry check and silences the warning. Previously symmetry was not checked at all.
I'd like to go one step further and
raise ValueError
when the covariance matrix isn't symmetric or PSD, of course, with the keyword args there so this can be bypassed. What do y'all think about that?old discussions:
#1821
http://mail.scipy.org/pipermail/numpy-discussion/2009-September/045255.html