-
Notifications
You must be signed in to change notification settings - Fork 64
Add New Metadata and Pattern Features #2271
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
base: main
Are you sure you want to change the base?
Add New Metadata and Pattern Features #2271
Conversation
import onnx | ||
|
||
from onnxscript import ir | ||
from onnxscript import rewriter |
Check notice
Code scanning / CodeQL
Unused import Note
#else: | ||
# vp_outputs = builder.__getattr__(node.op_type)(*ninputs) |
Check notice
Code scanning / CodeQL
Commented-out code Note
|
||
return ReplacementPatternGraph(g) | ||
|
||
def get_iterator_index(self): |
Check notice
Code scanning / CodeQL
Explicit returns mixed with implicit (fall through) returns Note
# Skip the (all) Activation inputs (have been swapped to beginning of the list) | ||
for index in range(activations, len(nodes[0].inputs)): | ||
inputs = [] | ||
producers = [] |
Check notice
Code scanning / CodeQL
Unused local variable Note
cinput = LoopBody.function.inputs[index] | ||
noutput = vdisconnect(copy.copy(cinput)) | ||
noutput._uses = {} | ||
update_node_outputs = False |
Check notice
Code scanning / CodeQL
Unused local variable Note
inputs.add(ninput) | ||
elif any(ninput is init for init in node.graph.initializers): | ||
initializers.add(ninput) | ||
elif ninput.producer() == None: |
Check notice
Code scanning / CodeQL
Testing equality to None Note
@@ -0,0 +1,190 @@ | |||
import pytest |
Check notice
Code scanning / CodeQL
Unused import Note test
import ast | ||
|
||
import onnx | ||
from onnxscript import script |
Check notice
Code scanning / CodeQL
Unused import Note test
# for rule in tracer.best_matches_map: | ||
# matches = tracer.best_matches_map[rule] | ||
# for match in matches: | ||
# print(f'Reason: {match.match_result.reason}') | ||
# print(f'root_node: {match.root_node}') | ||
# pdb.set_trace() |
Check notice
Code scanning / CodeQL
Commented-out code Note test
# for node in ir.traversal.RecursiveGraphIterator(mypipeline_model.graph): | ||
# if node.domain == '': | ||
# print(node) |
Check notice
Code scanning / CodeQL
Commented-out code Note test
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
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.
lintrunner found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
|
||
|
||
def is_initializer(value): | ||
return input.producer() == None |
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.
When a value is a graph input, its producer is also None. I will add a method to make this check more robust
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.
usage.add("EXTERNAL") | ||
return usage | ||
|
||
def find_subgraph_inputs(nodes): |
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.
@jsmonson could you share how you use this function?
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 see. It's for constructing the graph view
else: | ||
return self.class_metadata[depth] | ||
|
||
class PytorchHierarchyNode: |
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 useful! Let me see how we can provide the functionality to users.
def append_output_to_node(node, output): | ||
output._producer = node | ||
output._index = node.outputs[-1]._index + 1 | ||
node._outputs = (*node._outputs, output) | ||
node._num_outputs = len(node._outputs) | ||
|
||
def prepend_output_to_node(node, output): | ||
output._producer = node | ||
output._index = 0 | ||
for outp in node._outputs: | ||
outp._index += 1 | ||
node._outputs = (output, *node._outputs) | ||
node._num_outputs = len(node._outputs) |
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.
We should add this to node so there is no need to modify internal states
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.
In fact we recommend creating new nodes if you want to update inputs and outputs. This way invariance of the graph is maintained. You may consider accumulating the inputs and outputs into two lists before constructing the node.
Ident = ir.Node(domain='', | ||
op_type='Identity', | ||
inputs = [cinput], | ||
outputs = [noutput], | ||
num_outputs =1) | ||
LoopBody.function.append(Ident) | ||
|
||
#Add Output to Function Call Nodes | ||
for i,node in enumerate(nodes): | ||
output_copy = copy.copy(noutput) | ||
|
||
#preserve single_assignment | ||
output_copy.name += f'_{i}' | ||
append_output_to_node(node,output_copy) |
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.
Is there a way to collect all outputs before constructing the node?
vmap[input] = ValuePattern(input.name) | ||
|
||
for init in graph.initializers: | ||
vmap[init] = ValuePattern(init.name) |
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 like you want to map constants and initializers to unconstrained variables in the pattern? I wonder if it would make sense to map them to "Constants" in the pattern that require a matching contstant-value in the graph for a successful match? That makes reasonable, at least for simple and small constants. If it should be abstracted, wouldn't it be better for the user themselves to do that explicitly by mapping them to graph inputs?
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.
Yes. Thanks for this suggestion. I think we should do this. This would provide a good level of control for the user.
ninputs.append(vmap[ninput]) | ||
|
||
#if len(node.outputs) > 1: | ||
vp_outputs = builder.__getattr__(node.op_type)(*ninputs,_domain=node.domain, _outputs=len(node.outputs)) |
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.
So, the attributes of the node are abstracted away, and not matched?
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.
Good point. We ought to match the attributes. I'll make these changes.
This PR will add new metadata and pattern features.