-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
[Relay][Testing] Relay-to-Python compilation #3156
Conversation
There's a rather annoying issue in handling ADTs: This makes it really hard to build ADT values in this translation. Granted, I could pass the Relay module as a variable directly to A simple approach would be a string tag (e.g., '{name of ADT}_{name of constructor}_{index in ADT definition}'), but there are other choices. I think simplifying the representation would be better for future interoperability (like with @MarisaKirisame's ahead-of-time compiler). *Now that I've thought of it, I'll hack in this variant but I would prefer that we change the representation |
conda/tvm/meta.yaml
Outdated
@@ -41,6 +41,7 @@ requirements: | |||
- python | |||
- {{ pin_compatible('numpy') }} | |||
- decorator | |||
- astor |
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.
Could someone more experienced with managing TVM's dependencies tell me if this is the right manner to indicate a new Python dependency? astor
is only needed for testing (I could possibly exclude it entirely, though it would be convenient to have it)
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.
Nevermind, @tqchen suggested it's better to exclude the new dependency if it's not used directly in tests (it shouldn't be; astor
was only meant for debugging)
I would appreciate reviews, if possible: @jroesch, @MarisaKirisame , @zhiics, @wweic |
I still dont get the problem. Cant you just take that constructor value? I thought they are only constructed when adt is decclard. |
LGTM |
Well it's not a "problem" in that I can pass the module into the generated Python program (as I describe in my above comment), but I would prefer not to have to do that and I think it would make it easier in the future to write code that has to produce or accept values produced by Relay. |
I'll post a review today. |
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.
Looks good to me so far. I will do a more careful review later. Please add some tests. Curious, it looks that ADT is missing here. How would you plan to support them as well?
return name | ||
|
||
|
||
# returns an variable AST node for the given Relay var depending on |
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.
# returns an variable AST node for the given Relay var depending on | |
# returns a variable AST node for the given Relay var depending on |
|
||
|
||
# converts the given Relay function into a Python function | ||
def convert_func_node(self, name_hint: str, func: Expr): |
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.
def convert_func_node(self, name_hint: str, func: Expr): | |
def convert_func_node(self, name_hint: str, func: Func): |
?
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 is probably a good idea to add finer-grained type checks. I will address those soon.
python/tvm/relay/op/op_attrs.py
Outdated
"""Attributes for nn.global_pool""" | ||
|
||
|
||
@register_relay_attr_node |
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.
Should these new attributes node change in a separate PR?
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 needed them for this one because of the way op calls are handled: If the attr node is not registered, then it is treated as just a tvm.NodeBase object and there is thus no keys()
method or any way to iterate through the attribute values.
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 no longer depend on these changes so I will move them to another PR as requested. Included here: #3175
def run_as_python(expr: Expr, mod=relay.Module()): | ||
'''Converts the given Relay expression into a Python script and | ||
executes it.''' | ||
py_ast = to_python_ast(expr, mod) |
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.
Where is to_python_ast
defined?
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 was a mistake, it should be calling to_python
above
return (ast.Name(id=gvar.name_hint, ctx=ast.Load()), []) | ||
|
||
|
||
def visit_let(self, letexp: Expr): |
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.
add a unit test
return (ast.Attribute(ref, attr='value', ctx=ast.Load()), defs) | ||
|
||
|
||
def visit_ref_write(self, write: Expr): |
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.
Add a unit test
Yes, I will be adding ADTs today, along with tests. Thanks for the reviews |
Unfortunately it looks like Python's |
Next on the agenda: adding tests, as I have resolved the issue of |
@MarisaKirisame I followed the approach of your AoT compiler for lowering op calls, I would appreciate if you could look at that code. This has let me eliminate the call to Relay's interpreter from inside the generated code, so I will now be able to move the changes to attr nodes to their own PR because I don't use them here. |
@slyubomirsky I will looked later today/tmr morning. |
Yeah, fair enough on calling it a compilation. I called it a translation because I expected it to be much more modest in scope than it ultimately was. I can rename the file accordingly |
Now I have tests running for just about every feature of the translation; I will continue to add them but think it's worth looking over in greater detail. The conversion has ended up being substantially more complicated than I was hoping it would be, so I hope it is not self-defeating in that regard. |
@MarisaKirisame do you know if there's any way I could avoid running fusion but still be able to lower operators? I'd prefer this compiler not to have to depend on complicated Relay passes because its purpose is to test Relay |
@slyubomirsky fuse with opt-level = 0 |
Good idea, I'll do that too. |
b6e502a
to
5ffbacd
Compare
@zhiics @wweic please do another round of review and https://docs.tvm.ai/contribute/code_review.html#approve-and-request-changes-explicitly |
Sorry for missing this. I will do another review tonight. |
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.
lgtm. some minor comments.
return name | ||
|
||
|
||
# returns n variable AST node for the given Relay var depending on |
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.
# returns n variable AST node for the given Relay var depending on | |
# returns a variable AST node for the given Relay var depending on |
return converter.convert(expr) | ||
|
||
|
||
def run_as_python(expr: Expr, mod=relay.Module(), target=tvm.target.create('llvm')): |
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.
set mod to None
and initialize it to relay..Module()
in function body so every expression is evaluated in a fresh module. https://stackoverflow.com/a/33573262
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.
Ah, very good to know! I will change it
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.
Only some nitpicks, otherwise ltgm.
return ast.fix_missing_locations(ast.Module(body=body)) | ||
|
||
|
||
def optimize(self, prog: Expr): |
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.
Should we add a todo here to move these passes to the pass manager?
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.
Ah, I haven't been in the loop on that. If the pass manager is the right way to do these, then we should use it
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's not merged yet... The TODO just helps us to track it.
return re.sub(r'\W', '', name) | ||
|
||
|
||
# generates a unique variable name starting from the hint |
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.
Should we keep the doc format consistent, i.e. moving them under def
?
Same to the functions below.
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.
For things that were meant as purely internal functions, I just put the comments on top but I will change it to be consistent, thanks for the feedback.
Thank you for the feedback! |
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.
thanks.
…ccommodate necessary demands
b9d6d77
to
3be301d
Compare
* First pass at Relay-to-Python converter testing utility * Indicate astor as a dependency * Add astor dep to host as well * Typos and small bugs * Handle ADTs and matching in Python conversion * Remove any dependency on ast.parse * Eliminate unnecessary type var field in Python version of ConstructorValue (already gone on C++ side) * Update constructor value, fix syntax errors * Don't forget keywords arg on Call nodes * Fix some incorrect calls to ast nodes * Fix more calls, a little more cleaning up * Missing cases in attr conversion * Lower op calls instead of running them through interpreter, as in @MarisaKirisame's AoT compiler * We do still need the module * Remove changes to op attrs: Will PR separately * Smoke test and corrections * More tests and fixes * Ensure imports are properly global in generated Python code * Add unit tests for refs * Add unit test for tuple indexing * Add unit test for if expression * Remove astor dependency * Remove astor from meta.yaml too * Fix if test and add basic local function test * Add global function test, refactor earlier tests * Correct 'clause' field in ADT so Python and C++ field names match * More fixes and tests for matching and constructors * Dramatically simplify matching: no need for a thunk * Improve ref writing test * Ensure local recursion works * cleanup * Add test for global recursion * Add test for higher-order calls * Get ops working, add basic tests * Remove accidentally duplicated test * More docstrings to appease pylint * Forgot to fix a test using constructor values * Reduce optimization level in fusion and fix tuple input to operators * Test op with tuple output, fix tuple output code * Add unit test for batch norm * Add a couple more tricky test cases * Correct nat constructor to drop unnecessary field * Fix the op attrs file (accidentally reduced it) * Address review comments * Adapt to new ConstructorValue representation (no more runtime dep on module) * Use pass manager and updated interfaces. Extend module.from_expr to accommodate necessary demands * Use sequential return value * Lift out nested conditionals * Replace triple single quotes with triple double quotes * Use main variable instead of entry_func
* First pass at Relay-to-Python converter testing utility * Indicate astor as a dependency * Add astor dep to host as well * Typos and small bugs * Handle ADTs and matching in Python conversion * Remove any dependency on ast.parse * Eliminate unnecessary type var field in Python version of ConstructorValue (already gone on C++ side) * Update constructor value, fix syntax errors * Don't forget keywords arg on Call nodes * Fix some incorrect calls to ast nodes * Fix more calls, a little more cleaning up * Missing cases in attr conversion * Lower op calls instead of running them through interpreter, as in @MarisaKirisame's AoT compiler * We do still need the module * Remove changes to op attrs: Will PR separately * Smoke test and corrections * More tests and fixes * Ensure imports are properly global in generated Python code * Add unit tests for refs * Add unit test for tuple indexing * Add unit test for if expression * Remove astor dependency * Remove astor from meta.yaml too * Fix if test and add basic local function test * Add global function test, refactor earlier tests * Correct 'clause' field in ADT so Python and C++ field names match * More fixes and tests for matching and constructors * Dramatically simplify matching: no need for a thunk * Improve ref writing test * Ensure local recursion works * cleanup * Add test for global recursion * Add test for higher-order calls * Get ops working, add basic tests * Remove accidentally duplicated test * More docstrings to appease pylint * Forgot to fix a test using constructor values * Reduce optimization level in fusion and fix tuple input to operators * Test op with tuple output, fix tuple output code * Add unit test for batch norm * Add a couple more tricky test cases * Correct nat constructor to drop unnecessary field * Fix the op attrs file (accidentally reduced it) * Address review comments * Adapt to new ConstructorValue representation (no more runtime dep on module) * Use pass manager and updated interfaces. Extend module.from_expr to accommodate necessary demands * Use sequential return value * Lift out nested conditionals * Replace triple single quotes with triple double quotes * Use main variable instead of entry_func
* First pass at Relay-to-Python converter testing utility * Indicate astor as a dependency * Add astor dep to host as well * Typos and small bugs * Handle ADTs and matching in Python conversion * Remove any dependency on ast.parse * Eliminate unnecessary type var field in Python version of ConstructorValue (already gone on C++ side) * Update constructor value, fix syntax errors * Don't forget keywords arg on Call nodes * Fix some incorrect calls to ast nodes * Fix more calls, a little more cleaning up * Missing cases in attr conversion * Lower op calls instead of running them through interpreter, as in @MarisaKirisame's AoT compiler * We do still need the module * Remove changes to op attrs: Will PR separately * Smoke test and corrections * More tests and fixes * Ensure imports are properly global in generated Python code * Add unit tests for refs * Add unit test for tuple indexing * Add unit test for if expression * Remove astor dependency * Remove astor from meta.yaml too * Fix if test and add basic local function test * Add global function test, refactor earlier tests * Correct 'clause' field in ADT so Python and C++ field names match * More fixes and tests for matching and constructors * Dramatically simplify matching: no need for a thunk * Improve ref writing test * Ensure local recursion works * cleanup * Add test for global recursion * Add test for higher-order calls * Get ops working, add basic tests * Remove accidentally duplicated test * More docstrings to appease pylint * Forgot to fix a test using constructor values * Reduce optimization level in fusion and fix tuple input to operators * Test op with tuple output, fix tuple output code * Add unit test for batch norm * Add a couple more tricky test cases * Correct nat constructor to drop unnecessary field * Fix the op attrs file (accidentally reduced it) * Address review comments * Adapt to new ConstructorValue representation (no more runtime dep on module) * Use pass manager and updated interfaces. Extend module.from_expr to accommodate necessary demands * Use sequential return value * Lift out nested conditionals * Replace triple single quotes with triple double quotes * Use main variable instead of entry_func
For a simple testing utility, @jroesch and I decided that it would be useful to have a translation of Relay programs into equivalent Python code that we could use as an oracle for fuzzing.
Here I have written an attempt at such a translation. The Python code it produces is meant to match the intended semantics of Relay very closely rather than be performant, so there are some ineffeciencies that a smarter implementation could eliminate through analysis (translating let bindings as functions, for example). Again, the point is for the Python code to be evidently correct rather than an implementation that we would use for anything other than testing purposes.
There are a couple of open issues though: