Skip to content

Conversation

@leafs1
Copy link
Contributor

@leafs1 leafs1 commented Jun 27, 2025

Summary

The existing capability-based partitioner in PyTorch's FX module lacks the ability to specify node dependencies that must be partitioned together. This can lead to incorrect partitioning of dynamically quantized linear patterns, resulting in the loss of quantization semantics. For example, in a graph with shared QDQ (Quantize-Dequantize) chains, the partitioner may incorrectly separate nodes that should remain together, leading to incorrect execution semantics.

This PR addresses this issue by adding a new group-based partitioner that allows users to specify groups of nodes that must be partitioned together.

Test plan

I've created test cases that replicate existing QDQ tests, as well as additional graphs with different node dependencies. These tests aim to verify that the new partitioner correctly groups nodes together based on the specified dependencies.

@pytorch-bot
Copy link

pytorch-bot bot commented Jun 27, 2025

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/12072

Note: Links to docs will display an error until the docs builds have been completed.

❌ 2 Cancelled Jobs

As of commit 115cdfa with merge base c778063 (image):

CANCELLED JOBS - The following jobs were cancelled. Please retry:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 27, 2025
@leafs1 leafs1 force-pushed the partitioner branch 2 times, most recently from 8397b92 to afefd1d Compare June 27, 2025 21:20
@leafs1
Copy link
Contributor Author

leafs1 commented Jun 27, 2025

@pytorchbot label "release notes: none"

@pytorch-bot pytorch-bot bot added the release notes: none Do not include this in the release notes label Jun 27, 2025
@mergennachin mergennachin added release notes: exir Changes to any dialects and passes on these dialects, such as memory planning and removed release notes: none Do not include this in the release notes labels Jun 30, 2025
@digantdesai
Copy link
Contributor

For example, in a graph with shared QDQ (Quantize-Dequantize) chains, the partitioner may incorrectly separate nodes that should remain together

Still not fully grasped the motivation, can you explain this a bit more perhaps with an example? And why the existing greedy partitioner merging logic from the CapabilityBasedParitioner would split these nodes? Thanks.

Copy link
Contributor

@digantdesai digantdesai left a comment

Choose a reason for hiding this comment

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

Looks good, left some comments. Mainly around rationale and testing. I assume we will start using this in the XNNPACKPartitioner, if yes, that might help with testing.

allowed_single_node_partition_ops: Optional[Sequence[str]] = None,
node_groups: List[List[Node]] = None,
) -> None:
super().__init__(
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Add a doc script


return False

def find_nodes_by_names(
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
def find_nodes_by_names(
def _find_nodes_by_names(

return result

def create_model(self, model):
return model()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return model()
return model().eval()

[set(node_group) for node_group in node_groups] if node_groups else None
)
self.node_to_group = collections.defaultdict(int)
self.all_nodes_in_groups = set()
Copy link
Contributor

Choose a reason for hiding this comment

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

what if a node is in two groups? Should we check against that?

if self.node_groups:
for i, group in enumerate(self.node_groups):
# Create a partition for each group
partition_id = next(new_partition_id)
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the rationale for not allowing two node_groups in the same partition? Or can they get merged later, still reading...

)
self.check_partition(
partitions, {"add", "linear_2", "fake_quantize_per_tensor_affine_1"}
)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't believe these tests are sufficient to cover the rather huge propose_partitions method. I propose, in addition to these, can we a lot more tests with node_groups = None and compare the partitioned output with CapabilityBasedPartitioner?

@leafs1 leafs1 force-pushed the partitioner branch 2 times, most recently from bfb6bda to 5901086 Compare July 3, 2025 17:38
@leafs1
Copy link
Contributor Author

leafs1 commented Jul 3, 2025

For example, in a graph with shared QDQ (Quantize-Dequantize) chains, the partitioner may incorrectly separate nodes that should remain together

Still not fully grasped the motivation, can you explain this a bit more perhaps with an example? And why the existing greedy partitioner merging logic from the CapabilityBasedParitioner would split these nodes? Thanks.

Here's the initial github issue with an example #11447. The idea is that the current greedy partitioner when dealing with a quantized graph can partition the dq/q nodes in different partitions (example partition 1 has q and partition 2 has the op and the dq). As a result, the semantics of the quantized op is lost in the graph.

Copy link
Contributor

@mcr229 mcr229 left a comment

Choose a reason for hiding this comment

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

@leafs1 thanks for all these extra tests michael. I do believe it is well tested. Just as a follow up let's test for faulty cases (enforced groups might not be valid (because of dependencies)). And let's also put up a PR wiring this up to xnnpack so we can see how this fairs.

Not sure if this is added as well. But let's put some tests up where compatibility and group based partitioner differ in their outputted partition. Specifically in a quantization use case where a quantized node is moved out of its necessary dependency.

@leafs1 leafs1 merged commit 4e50966 into pytorch:main Jul 12, 2025
95 of 97 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. release notes: exir Changes to any dialects and passes on these dialects, such as memory planning

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants