-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
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
ENH: Allow specyfing inverse covariance of a multivariate normal distribution #16002
ENH: Allow specyfing inverse covariance of a multivariate normal distribution #16002
Conversation
@siddhantwahal thanks for your patience here. Personally, I've been focusing on fixing bugs... When that's a little more under control, I know there are a lot of enhancements waiting! |
@mdhaber no worries! I think gh-15675 can be broken into two tasks:
To enable (1) after this PR:
I think that's it. One possible UX is (assuming the eigen decomposition is [nav] In [3]: cov_info = CovInfo(
...: sqrt_cov=u @ np.sqrt(d),
...: sqrt_inv_cov=u @ np.sqrt(1 / d),
...: log_det_cov=np.sum(np.log(d)),
...: rank=len(d)
...: )
[nav] In [4]: mvn = multivariate_normal(mean=np.zeros(10_000), cov_info=cov_info) The UX could be enhanced by adding factory methods to create |
Thanks @siddhantwahal for your patience. I'll take a look in June if nobody gets to this first. |
scipy/stats/_multivariate.py
Outdated
self._cov = cov | ||
self._inverse_cov = inverse_cov | ||
|
||
@cached_property |
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 like the idea of this class. I wonder why it doesn't go a little further. Why not accept all of the information the user can provide in the initializer and store all of it as private attributes in the initializer (e.g. self._sqrt_cov = _sqrt_cov
), then have public cached_property
s for all of these attributes that computes them (lazily, in effect) in the most efficient way, given whatever information is available?
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.
Before I go much further with this review, I want to check what you think of the idea of refactoring this so that all the logic about the covariance matrix (including the stuff in process_parameters
, _process_cov_shaped
) goes in the _CovInfo
class, and _process_parameters
would instantiate the _CovInfo
object rather than doing that separately in every method.
Or really, what is the distinction between _CovInfo
and _PSD
/ what should it be? Maybe I haven't looked deeply enough, but it seems like it would be nice to have one class that accepts all sorts of different representations of a covariance matrix and has methods that do all the things you want to do with a covariance matrix. If we combined the things we like from this PR and the existing _PSD
class to make a nice class representing a covariance matrix, maybe we could also endow other multivariate distributions (e.g. multivariate_t
) with these same features, but with minimal changes to existing code?
scipy/stats/_multivariate.py
Outdated
@@ -437,7 +537,31 @@ def _process_quantiles(self, x, dim): | |||
|
|||
return x | |||
|
|||
def _logpdf(self, x, mean, prec_U, log_det_cov, rank): | |||
def _create_cov_info(self, cov, inverse_cov, allow_singular=True): |
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.
Along the lines of https://github.com/scipy/scipy/pull/16002/files#r906859132, I think this logic could go in _CovInfo
. Similar logic could be added for eigendecomposition or variances and rotation angles.
What do you think about this comment #16002 (review) @siddhantwahal? (Sorry it took me a while to get to this!) |
Thanks a lot for the thoughtful review @mdhaber. I was swamped with preparing for our Scipy 2022 tutorial so haven't had a chance to consider or respond to your suggestion. I plan on resuming work on this in the next few weeks! |
@siddhantwahal oh, are you here? Hope to see you at the sprint tomorrow! |
Apologies for the delay here @mdhaber.
I was envisioning However, this separation forces us to specify the
It's not yet clear to me whether we could or even should have one class to do this. For example,
That should be possible with the latest implementation too,
Thanks for the suggestion of computing |
[skip azp] [skip actions]
I was hoping Sphinx would automatically document the attributes based on the docstrings of the properties, but that's not happening. Direct link to documentation: Thoughts for how the properties should be documented (so that they are rendered)? |
@tirthasheshpatel I thought it would be good to get your eyes on this. IIRC, you were interested in vectorization over many covariance matrices for the |
I started to look into this but didn't get very far. Sphinx is picking up the properties, (see the My working hypothesis is that |
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 for the late review! Just a few nitpicky comments otherwise the implementation looks good.
About |
Thanks @tirthasheshpatel! I implemented these suggestions. As it turns out, we don't need to do anything special with |
Failures appear to be unrelated. |
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.
The tests look very strong! Just one nitpick and I think this PR is good to go. Thanks @mdhaber, @siddhantwahal!!
Thanks @tirthasheshpatel! Made the change. Please feel free to squash-merge with me as a co-author. What would you be most interested in reviewing next? We can specify covariance matrices using:
And I can add LDL after we add these. Presumably, I should add these in short, separate PRs to keep things simple? |
I think the diagonal one should be a bit easier. We can start with that!
That sounds good! |
Merged! Thanks @mdhaber @siddhantwahal! |
Thanks, Tirth! |
|
||
class Covariance: | ||
""" | ||
Representation of a 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.
This is really missing some documentation IMHO.
Also, from the naming of the subclasses, it seems that classmethods would be more appropriate. Then you would have Covariance.from_precision
instead of CovViaPrecision
.
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 wasn't public before. The subclasses had more information about how they were working. When we go to the classmethod idea, I can add some more information to the Covariance
class itself.
Reference issue
Closes #11053, #11772
What does this implement/fix?
This PR allows specifying the inverse covariance of a multivariate normal in addition or instead of the covariance by adding the
inverse_cov
keyword argument.To provide functionality with the covariance or its inverse, essential information from the supplied matrix is extracted into a
_CovInfo
object. This object is then passed to private helper methods such asmultivariate_normal_gen._logpdf
.This PR also introduces the private
multivariate_normal_gen._rvs
method. This introduction is only needed to solve #11772 and is a little orthogonal to the changes needed to solve #11053. However, the introduction of the_CovInfo
allows solving #11772 in just a few lines, so I've gone ahead and included them here.If desired, the
_CovInfo
class could be included in the public API so that advanced users can instantiate it and pass it as a keyword argument instead. For example, instead of only being restricted to supplying the inverseusers would get complete control over the description of the covariance matrix through its square root factors:
Thus, this PR also makes progress towards #15675.
Additional information
This PR supersedes #12184 which should be closed if this is merged.