-
Couldn't load subscription status.
- Fork 5.9k
[Auto Parallel] Add spmd rule No.6 for unique ops. #72824
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
Changes from all commits
f313ed0
35189aa
55ff07d
994a4e4
b89941c
c932041
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| /* Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. */ | ||
|
|
||
| #include "paddle/phi/infermeta/spmd_rules/unique.h" | ||
| #include "glog/logging.h" | ||
| #include "paddle/phi/core/distributed/auto_parallel/dist_attr.h" | ||
| #include "paddle/phi/infermeta/spmd_rules/spmd_rule_macro_define.h" | ||
| #include "paddle/phi/infermeta/spmd_rules/utils.h" | ||
|
|
||
| namespace phi { | ||
| namespace distributed { | ||
|
|
||
| SpmdInfo UniqueInferSpmd(const DistMetaTensor& x, | ||
| bool return_index, | ||
| bool return_inverse, | ||
| bool return_counts, | ||
| const std::vector<int>& axis, | ||
| DataType dtype) { | ||
| // Verify input args | ||
| EXTRACT_SHAPE_AND_DIST_ATTR(x); | ||
| std::vector<int64_t> x_dims_mapping_dst(x_ndim, -1); | ||
| std::vector<int64_t> out_dims_mapping_dst(x_dims_mapping_dst); | ||
| TensorDistAttr x_dist_attr_dst = CopyTensorDistAttrForOutput(x_dist_attr_src); | ||
| x_dist_attr_dst.set_dims_mapping(x_dims_mapping_dst); | ||
|
|
||
| if (axis.empty()) { | ||
| out_dims_mapping_dst = {-1}; | ||
| } | ||
| TensorDistAttr out_dist_attr_dst = | ||
| CopyTensorDistAttrForOutput(x_dist_attr_src); | ||
| out_dist_attr_dst.set_dims_mapping(out_dims_mapping_dst); | ||
|
|
||
| TensorDistAttr indices_dist_attr_dst = TensorDistAttr(); | ||
| if (return_index) { | ||
| indices_dist_attr_dst = CopyTensorDistAttrForOutput(x_dist_attr_src); | ||
| indices_dist_attr_dst.set_dims_mapping({-1}); | ||
| } | ||
|
|
||
| TensorDistAttr inverse_dist_attr_dst = TensorDistAttr(); | ||
| if (return_inverse) { | ||
| inverse_dist_attr_dst = CopyTensorDistAttrForOutput(x_dist_attr_src); | ||
| inverse_dist_attr_dst.set_dims_mapping({-1}); | ||
| // TODO(dev): https://github.com/PaddlePaddle/Paddle/issues/72822 | ||
| // if (axis.empty()) { | ||
| // inverse_dist_attr_dst.set_dims_mapping(x_dims_mapping_dst); | ||
| // } | ||
| } | ||
|
|
||
| TensorDistAttr counts_dist_attr_dst = TensorDistAttr(); | ||
| if (return_counts) { | ||
| counts_dist_attr_dst = CopyTensorDistAttrForOutput(x_dist_attr_src); | ||
| counts_dist_attr_dst.set_dims_mapping({-1}); | ||
| } | ||
|
|
||
| VLOG(4) << "UniqueInferSpmd: All input and output TensorDistAttr are set to " | ||
| "fully replicated status."; | ||
| return {{x_dist_attr_dst}, | ||
| {out_dist_attr_dst, | ||
| indices_dist_attr_dst, | ||
| inverse_dist_attr_dst, | ||
| counts_dist_attr_dst}}; | ||
| } | ||
|
|
||
| SpmdInfo UniqueInferSpmdStatic(const DistMetaTensor& x, | ||
| bool return_index, | ||
| bool return_inverse, | ||
| bool return_counts, | ||
| const std::vector<int>& axis, | ||
| DataType dtype, | ||
| bool is_sorted) { | ||
| return UniqueInferSpmd( | ||
| x, return_index, return_inverse, return_counts, axis, dtype); | ||
| } | ||
| } // namespace distributed | ||
| } // namespace phi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| /* Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "paddle/phi/core/distributed/auto_parallel/dist_meta_tensor.h" | ||
| #include "paddle/phi/core/distributed/type_defs.h" | ||
|
|
||
| namespace phi { | ||
| namespace distributed { | ||
|
|
||
| SpmdInfo UniqueInferSpmd(const DistMetaTensor& x, | ||
| bool return_index, | ||
| bool return_inverse, | ||
| bool return_counts, | ||
| const std::vector<int>& axis, | ||
| DataType dtype); | ||
|
|
||
| SpmdInfo UniqueInferSpmdStatic(const DistMetaTensor& x, | ||
| bool return_index, | ||
| bool return_inverse, | ||
| bool return_counts, | ||
| const std::vector<int>& axis, | ||
| DataType dtype, | ||
| bool is_sorted); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why should we need a distinct "Static" interface ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because I see the unique op in dynamic mode and static mode has different parameters. In my mind, auto parallel is support with two modes, but i'am don't sure how framework to do it, so I add this interface with 'static' suffex and register in 'paddle/phi/ops/yaml/inconsistent/static_ops.yaml'. It's only my guess. Can you tell me more information about it, will I need delete it? Thanks ! |
||
| } // namespace distributed | ||
| } // namespace phi | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import unittest | ||
| from collections import OrderedDict | ||
|
|
||
| from paddle.distributed.auto_parallel.static.dist_attribute import ( | ||
| DistTensorSpec, | ||
| TensorDistAttr, | ||
| ) | ||
| from paddle.distributed.fleet import auto | ||
| from paddle.framework import convert_np_dtype_to_dtype_, core | ||
|
|
||
|
|
||
| class TestUniqueSPMDRule(unittest.TestCase): | ||
| def setUp(self): | ||
| self.rule = core.get_phi_spmd_rule("unique") | ||
| x_shape = [4, 8] | ||
| process_mesh = auto.ProcessMesh(mesh=[[0, 1], [2, 3]]) | ||
|
|
||
| x_tensor_dist_attr = TensorDistAttr() | ||
| x_tensor_dist_attr.dims_mapping = [1, 0] | ||
| x_tensor_dist_attr.process_mesh = process_mesh | ||
| self.x_dist_tensor_spec = DistTensorSpec(x_shape, x_tensor_dist_attr) | ||
| self.attrs = OrderedDict() | ||
| self.attrs["return_index"] = True | ||
| self.attrs["return_inverse"] = True | ||
| self.attrs["return_counts"] = True | ||
| self.attrs["axis"] = [] | ||
| self.attrs['dtype'] = convert_np_dtype_to_dtype_("int32") | ||
|
|
||
| def test_infer_forward(self): | ||
| # return_index=True, return_inverse=True, return_counts=True, axis={} | ||
| # [0, -1] --> [-1,-1], [-1], [-1], [-1], [-1] | ||
| self.x_dist_tensor_spec.set_dims_mapping([0, -1]) | ||
| result_dist_attrs = self.rule.infer_forward( | ||
| self.x_dist_tensor_spec, | ||
| self.attrs["return_index"], | ||
| self.attrs["return_inverse"], | ||
| self.attrs["return_counts"], | ||
| self.attrs["axis"], | ||
| self.attrs['dtype'], | ||
| ) | ||
|
|
||
| self.assertEqual(len(result_dist_attrs), 2) | ||
| inferred_input_dist_attrs = result_dist_attrs[0] | ||
| inferred_output_dist_attrs = result_dist_attrs[1] | ||
|
|
||
| self.assertEqual(len(inferred_input_dist_attrs), 1) | ||
| self.assertEqual(len(inferred_output_dist_attrs), 4) | ||
|
|
||
| self.assertEqual(inferred_input_dist_attrs[0].dims_mapping, [-1, -1]) | ||
| self.assertEqual(inferred_output_dist_attrs[0].dims_mapping, [-1]) | ||
| self.assertEqual(inferred_output_dist_attrs[1].dims_mapping, [-1]) | ||
| self.assertEqual(inferred_output_dist_attrs[2].dims_mapping, [-1]) | ||
| self.assertEqual(inferred_output_dist_attrs[3].dims_mapping, [-1]) | ||
|
|
||
| # return_index=True, return_inverse=True, return_counts=True, axis={0} | ||
| # [0, -1] --> [-1,-1], [-1,-1], [-1], [-1], [-1] | ||
| self.x_dist_tensor_spec.set_dims_mapping([0, -1]) | ||
| self.attrs["axis"] = [0] | ||
| result_dist_attrs = self.rule.infer_forward( | ||
| self.x_dist_tensor_spec, | ||
| self.attrs["return_index"], | ||
| self.attrs["return_inverse"], | ||
| self.attrs["return_counts"], | ||
| self.attrs["axis"], | ||
| self.attrs['dtype'], | ||
| ) | ||
|
|
||
| self.assertEqual(len(result_dist_attrs), 2) | ||
| inferred_input_dist_attrs = result_dist_attrs[0] | ||
| inferred_output_dist_attrs = result_dist_attrs[1] | ||
|
|
||
| self.assertEqual(len(inferred_input_dist_attrs), 1) | ||
| self.assertEqual(len(inferred_output_dist_attrs), 4) | ||
|
|
||
| self.assertEqual(inferred_input_dist_attrs[0].dims_mapping, [-1, -1]) | ||
| self.assertEqual(inferred_output_dist_attrs[0].dims_mapping, [-1, -1]) | ||
| self.assertEqual(inferred_output_dist_attrs[1].dims_mapping, [-1]) | ||
| self.assertEqual(inferred_output_dist_attrs[2].dims_mapping, [-1]) | ||
| self.assertEqual(inferred_output_dist_attrs[3].dims_mapping, [-1]) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
Uh oh!
There was an error while loading. Please reload this page.
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.
If the variable "axis" is not "None", the number of dimension of "indices" should be the same as that of the input "x" (so are "inverse" and "counts").
It does not make sense to simply set "dims_mapping" as {-1}.
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.
If the variable "axis" is not "None", according to the

UniqueRawInferMeta,they are 1D Tensor Along axisThere 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.
Oh you're right! I'm sorry to misunderstand the operator. Thanks for helping me understand it correctly!