Skip to content
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

Implement measurable transforms for division, subtraction and negation #144

Closed
wants to merge 3 commits into from

Conversation

ricardoV94
Copy link
Contributor

@ricardoV94 ricardoV94 commented Apr 29, 2022

Follow up to #26

This implements measurable rewrites for the following type of graphs:

x = at.random.gamma()
y = 1 / x
joint_logprob({y: y.type()})

As well as:

y = 5 / x
y = -x
y = 5 - x

It works by canonicalizing such operations to the form already understood by find_measurable_transforms (only a new condition for Reciprocals was added)

(5 / x) -> 5 * reciprocal(x)
(-x) -> x * (-1)
5 - x -> 5 + (x * (-1))

These canonicalizations are only applied when MeasurableVariables are involved to minimize disruption of the underlying graph

@ricardoV94 ricardoV94 added the rv-transforms Involves transforms applied to random variables label Apr 29, 2022
aeppl/opt.py Outdated Show resolved Hide resolved
@codecov
Copy link

codecov bot commented Apr 29, 2022

Codecov Report

Base: 94.82% // Head: 94.55% // Decreases project coverage by -0.27% ⚠️

Coverage data is based on head (df99f5b) compared to base (46f6775).
Patch coverage: 84.48% of modified lines in pull request are covered.

❗ Current head df99f5b differs from pull request most recent head 350a42e. Consider uploading reports for the commit 350a42e to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #144      +/-   ##
==========================================
- Coverage   94.82%   94.55%   -0.28%     
==========================================
  Files          12       12              
  Lines        1933     1835      -98     
  Branches      289      276      -13     
==========================================
- Hits         1833     1735      -98     
- Misses         55       56       +1     
+ Partials       45       44       -1     
Impacted Files Coverage Δ
aeppl/tensor.py 80.83% <82.60%> (-4.89%) ⬇️
aeppl/transforms.py 96.25% <91.66%> (-0.19%) ⬇️
aeppl/scan.py 94.73% <0.00%> (ø)
aeppl/abstract.py 100.00% <0.00%> (ø)
aeppl/rewriting.py
aeppl/censoring.py
aeppl/truncation.py 98.27% <0.00%> (ø)
aeppl/opt.py 94.00% <0.00%> (ø)
... and 3 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Copy link
Member

@brandonwillard brandonwillard left a 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 little more context to the description?

@ricardoV94
Copy link
Contributor Author

Can you add a little more context to the description?

Done

@brandonwillard brandonwillard added enhancement New feature or request graph rewriting Involves the implementation of rewrites to Aesara graphs labels May 1, 2022
@ricardoV94 ricardoV94 changed the title Implement Inverse measurable transform Implement measurable transforms for division, subtraction and negation May 2, 2022
@ricardoV94
Copy link
Contributor Author

Added other rewrites that cover negation and subtraction of MeasurableVariables

aeppl/transforms.py Show resolved Hide resolved
aeppl/transforms.py Show resolved Hide resolved
aeppl/transforms.py Show resolved Hide resolved
aeppl/transforms.py Show resolved Hide resolved
Copy link
Member

@brandonwillard brandonwillard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very unsure about these fundamental (implicit) canonicalization changes made to rational expressions. Those could really create issues down the road, and they're something that should probably be used in Aesara as well, or not at all. In other words, if they're justified here, why aren't they justified for general use in Aesara?

Anyway, we might be able to try going forward with this—after renaming that class—but I still think it needs more consideration, especially since adding explicit canonicalizations isn't usually necessary (i.e. functionality-specific local rewrites can make the relevant rational form considerations instead).

aeppl/tensor.py Outdated

@local_optimizer([Elemwise])
def measurable_div_to_reciprocal_product(fgraph, node):
r"""Convert divisions involving `MeasurableVariable`\s to products with their reciprocals."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to start using the notation/approach mentioned here to clearly specify our rewrites at a high-level.

Specifically, the numerator not being equal to one could be compactly conveyed this way, along with the basic form of the rewrite itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... forgot about this. Will do

aeppl/tensor.py Outdated Show resolved Hide resolved
aeppl/tensor.py Outdated Show resolved Hide resolved
aeppl/transforms.py Outdated Show resolved Hide resolved
@ricardoV94
Copy link
Contributor Author

ricardoV94 commented Jul 4, 2022

In other words, if they're justified here, why aren't they justified for general use in Aesara?

I think it makes sense to canonicalize in Aesara, except if weird float precision issues may crop up in general. We already have an issue opened for that, so if it goes through we can revert these ones here.

But even if we conclude they don't make sense in Aesara, here there are further context reasons to justify them. We know that in Aeppl most cases of unvalued RVs are meant to be replaced. If for some reason these canonicalizations are not desired, users can remove the rewrite from the standard database.

We also already have other canonicalizations that don't translate to measurable variables directly like the broadcast one.

The question is whether the trouble of affecting RV graphs that won't be replaced by value variables is larger than the cost of doing more refined intermediate rewrites that will be reverted automatically if unused...

@ricardoV94
Copy link
Contributor Author

Alternatively, if we convert all unvalued RVs to MeasurableVariables before applying these canonicalizations, they will be undone when they are not needed by the same machinery in here:

if rv_remapper.measurable_conversions:

aeppl/transforms.py Outdated Show resolved Hide resolved
@ricardoV94 ricardoV94 force-pushed the inverse_transform branch 2 times, most recently from 3d98da6 to 50a829e Compare September 12, 2022 09:12
@ricardoV94
Copy link
Contributor Author

@brandonwillard I have not addressed the concerns about intermediate rewrites that simply canonicalize intermediate variables, besides restricting those rewrites to "unvalued measurable variables".

IMO until we figure out what we want to do with unmeasured RandomVariables in our graphs (as discussed in #174 and #85) we could keep assuming they are there to be replaced anyway, in which case these rewrites don't do any damage and make it simpler to cover a wider range of measurable Elemwise operations.

Anyway, I re-requested your review to check that this is the only concern left and discuss it.

@brandonwillard
Copy link
Member

brandonwillard commented Sep 12, 2022

@brandonwillard I have not addressed the concerns about intermediate rewrites that simply canonicalize intermediate variables, besides restricting those rewrites to "unvalued measurable variables".

IMO until we figure out what we want to do with unmeasured RandomVariables in our graphs (as discussed in #174 and #85) we could keep assuming they are there to be replaced anyway, in which case these rewrites don't do any damage and make it simpler to cover a wider range of measurable Elemwise operations.

Anyway, I re-requested your review to check that this is the only concern left and discuss it.

We can always make these rewrites elective (i.e. a user must explicitly load/use them) for the time being; otherwise, adding rewrites that change the expectations of other rewrites (i.e. affect and/or determine canonical/normal forms) leads to serious downstream problems. For instance, if a / b is always converted to a * (1 / b) then subsequent rewrites expecting a / b (sub-)expressions are rendered useless. This could manifest as an inability to use certain basic Aesara rewrites with AePPL IR, or, at the very least, it could produce AePPL IR graphs that are inconsistent with the ones produced by Aesara for equivalent graphs.

Such "intermediate" rewrites can facilitate other rewrites that relate more directly to the desired functionality (i.e. the transforms in this PR), so the approach is very understandable. One important question that arises is these situations: "Can the intermediate rewrites be applied in isolation (i.e. not "committed" to the graph being rewritten) instead?".

In quite a few cases for which this isn't possible, I've found that the limiting factors involved an inability to perform reasonable graph comparisons (e.g. one would need to inefficiently walk both graphs). Core Aesara changes like aesara-devs/aesara#1110 and aesara-devs/aesara#1165 would address those issues.

In other cases, one needs intermediate rewrites to realize larger graph forms when, for example, there are no known sequences of "local" rewrites that will produce the desired forms consistently. Something like kanren can enumerate rewrite "trajectories" without making permanent changes to a graph, but that approach currently requires graph cloning (i.e. converting to etuples) and is only scalable when used in combination with some variety of memoization/tabling. We also don't have a consistent means of using Aesara rewrites in that context, so all the necessary rewrites need to be implemented in or converted to kanren goals. Core Aesara changes like aesara-devs/aesara#1082 and the use of Equality Saturation should allow us to address these cases more generally.

@ricardoV94
Copy link
Contributor Author

ricardoV94 commented Sep 13, 2022

Thanks for the reply.

I am not totally convinced why this matters here. Assuming we introduce other rewrites in the future that would try to match a = neg(x), I imagine they would also want to match b = mul(-1, x). Therefore such rewrite would need to 1) try to match both, in which case it doesn't matter that we changed from a to b or 2) save work and only match one, in which case it helps to have an "Aeppl canonicalized form" (b).

I understand why in Aesara we might not want to do that because of numerical stability. But here we are not dealing with direct computations yet, and we don't even know which, if any, IR form will lead to a more stable logprob (and the user much less), specially because this may be used as a building block for arbitrarily complex valued-variables.

Note that given the checks in place, the outputs of these intermediate rewrites will always be picked up by find_measurable_transforms and converted to a MeasurableVariable, so the final outcome would be completely equivalent if instead I expanded the find_measurable_transforms rewrite to be able to directly match different variations and skip the intermediate forms.

The only side-effect happens with unvalued-orphan RandomVariables which we haven't decided on a policy for yet.

Ricardo added 3 commits September 16, 2022 15:40
Adds rewrite that converts divisions with measurable variables to product with reciprocals, making the reciprocal measurable transform more widely applicable.
@rlouf rlouf marked this pull request as draft November 21, 2022 11:13
@brandonwillard
Copy link
Member

brandonwillard commented Dec 2, 2022

In general, it looks like the transform approach in #26 and this PR is rather buggy and acceptable changes to the rewrite orders cause numerous problems. After fixing something like #170 and running this, it becomes very apparent.

I'm going to close this and open a replacement PR that fixes the aforementioned, and the MeasurableElemwise approach as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request graph rewriting Involves the implementation of rewrites to Aesara graphs rv-transforms Involves transforms applied to random variables
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants