-
-
Notifications
You must be signed in to change notification settings - Fork 152
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
Address basic design and operational issues of ShapeFeature
#749
Comments
@kc611, tell me what you think about the proposed |
I assume you mean As of now, I don't see any reason why it won't work. I mean yeah, we'd just have to make sure those outputs don't show up on a interface level. I guess the biggest issue with directly adding these nodes into the normal One question however, usually a |
Exactly; that would be real work behind this idea. I think the whole idea ultimately boils down to merging or nesting distinct
I think I'll need an example to understand this situation clearly; however, there shouldn't be any rewrites that completely remove a tracked shape graph. A tracked shape graph should only be removed when the underlying Also, we shouldn't eagerly track every
Yes, we will always need to keep such a mapping. The only difference is that the mapping will now map Just to be clear, the "hidden" outputs thing isn't important; it's just a way to illustrate how we could track shapes that are seamlessly integrated into the |
ShapeFeature
has a few big redundancies in relation to the standard rewrite functionality and framework, and its logic is unnecessarily complicated for the functionality it provides. Its design also produces some avoidable limitations from which we're currently suffering—aside from ongoing maintenance headaches.These issues can be observed together within a single motivating example:
ShapeFeature
keeps adict
of graphVariable
s mapped to their inferred shapes (i.e. the outputs ofOp.infer_shape
). These shape graphs are not canonicalized until they're put in a graph (and a canonicalization pass is made). This complicates the use of results fromShapeFeature
by, for example, requiring independent and wasteful canonicalization rewrites in order to reasonably compare or derive information from the shapes it tracks.These issues are also relevant to #732.
Illustration of the Problem
A concrete example is
ShapeFeature.same_shape
. In #711, we had to add an explicit canonicalization step in order to make this functionality even remotely applicable outside of extremely trivial comparisons. These kinds isolated rewrites are highly undesirable because their results are not reusable and they will likely be repeated in multiple different places. (They're also the kind of rewrites we want to avoid in #732.)More specifically, if
ShapeFeature.same_shape
is called, a canonicalization of twoVariable
s' shapes is performed. Those results are discarded afterShapeFeature.same_shape
is finished. Later, ifShapeFeature.same_shape
is called again with one of the sameVariable
arguments, the same canonicalization is performed and discarded. Similarly, if one of thoseVariable
s is added to the "main"FunctionGraph
being rewritten, the same canonicalization passes will be applied to the same subgraph once again.It's important to realize that the context we're discussing technically involves two
FunctionGraph
s: the "main"FunctionGraph
that is utilizing theShapeFeature
, and an "ephemeral"FunctionGraph
used or created by—say—aesara.graph.opt_utils.optimize_graph
to perform a one-off canonicalization of a graph. This is exactly the situation inShapeFeature.same_shape
.When one remembers that all rewriting needs to be done in the context of a
FunctionGraph
, the main problem(s) arising from the concerns of this issue—and some big design problem(s) ofShapeFeature
itself—become a little more apparent.Possible Solution(s)
All the solutions I can think of right now involve the need for
ShapeFeature
to keep in-sync with theFunctionGraph
it serves. BecauseShapeFeature
is curating its own set of graphs outside of itsFunctionGraph
, those graphs are ignored by all the canonicalization passes that would automatically solve our problem.Also, notice that
ShapeFeature
has a lot of logic for performing updates on those graphs in response to updates fromFunctionGraph
. This situation—and the fact that its implementation is very opaque and unnecessarily difficult to follow—leads me to believe that the entire approach/idea ofShapeFeature
might be fundamentally flawed.Consider the (effective) functionality it currently provides:
Shape
Op
s with thetuple
results ofOp.infer_shape
(when available) or genericShape_i
Op
s,Variable
s in theFunctionGraph
(i.e.ShapeFeature.same_shape
)Its docstring shows that some of its previously intended functionality was fundamentally ill suited for a
Feature
: e.g. perform lifting rewrites and removingat.fill/second
s from a graph. Those two have already been obviated by standard rewrites in Aesara, and we should do the same for the remaining functionality.As far as the remaining functionality is concerned, 1. can be easily replaced by a few simple
local_optimizer
rewrites, and 3. is already trivial to implement as a stand-alone function. It's 2. that might need some special considerations.My first impression is that this kind of caching is the sole purpose of a
ShapeFeature
(if any). Unlike the currentShapeFeature
, there should be no complicated synching required between the graphs in its cache and theFunctionGraph
, and all rewrites applied to theFunctionGraph
should seamlessly apply to the graphs in the cache. The latter is the only thing that may require new functionality, and that would possibly involve changes to nothing other thanFunctionGraph
.If we were to add all the shape graphs tracked by a
ShapeFeature
as additional outputs in itsFunctionGraph
, every rewrite toFunctionGraph
would be applied to those graphs and our main issue would be resolved. Of course we would need to prune/remove those additional outputs when/if their respectiveVariable
s are removed from the graph—otherwise we would be doing unnecessary work from there on—but that's definitely a suitable responsibility for aShapeFeature
.While adding and removing new outputs to a
FunctionGraph
is technically supported right now, we wouldn't want to take this approach because it would alter the results expected by users ofFunctionGraph
s. In other words, there would be new, irrelevant, and unexpectedFunctionGraph.outputs
entries that other code would need to handle.Instead, we could consider adding some type of "hidden" or "internal" outputs functionality to
FunctionGraph
. It would only need to preserve the original outputs inFunctionGraph.outputs
and perhaps a few other interface-level results.The text was updated successfully, but these errors were encountered: