Files
paddlepaddle--paddle/paddle/phi/infermeta/spmd_rules/transpose.cc
T
2026-07-13 12:40:42 +08:00

218 lines
9.1 KiB
C++

/* 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. */
#include "paddle/phi/infermeta/spmd_rules/transpose.h"
#include "glog/logging.h"
#include "paddle/phi/core/distributed/auto_parallel/dist_attr.h"
#include "paddle/phi/core/distributed/auto_parallel/inferspmd_utils.h"
#include "paddle/phi/core/distributed/auto_parallel/utils.h"
#include "paddle/phi/infermeta/spmd_rules/utils.h"
namespace phi::distributed {
void BuildEinsumNotation(const size_t x_ndim,
std::vector<int> perm,
std::string* p_x_axes,
std::string* p_out_axes) {
std::string alphabet = "abcdefghijklmnopqrstuvwxyz";
// get einsum notation for x
*p_x_axes = alphabet.substr(0, x_ndim);
// convert perm
for (size_t i = 0; i < x_ndim; i++) {
if (perm[i] < 0) {
perm[i] += x_ndim;
}
}
// get einsum notation for out
*p_out_axes = "";
for (size_t i = 0; i < x_ndim; i++) {
p_out_axes->append(1, p_x_axes->at(perm[i]));
}
}
////////////////// InferMeta(Contains SPMD) Functions //////////////////
SpmdInfo TransposeInferSpmd(const DistMetaTensor& x,
const std::vector<int>& perm) {
// Step0: Verify input args based on transpose logic
std::vector<int64_t> x_shape = vectorize(x.dims());
size_t x_ndim = x_shape.size();
const TensorDistAttr& x_dist_attr_src = x.dist_attr();
std::vector<std::vector<int64_t>> x_dims_mapping =
x_dist_attr_src.multi_dims_mapping();
PADDLE_ENFORCE_EQ(
x_ndim,
x_dims_mapping.size(),
common::errors::InvalidArgument("The Tensor X's rank [%d] and X's "
"dims_mapping size [%d] are not matched.",
x_ndim,
x_dims_mapping.size()));
// check perm size
PADDLE_ENFORCE_EQ(
x_ndim,
perm.size(),
common::errors::InvalidArgument("The Tensor X's rank [%d] and "
"perm size [%d] are not matched.",
x_ndim,
perm.size()));
// Step1: Build Einsum Notation
std::string x_axes;
std::string out_axes;
BuildEinsumNotation(x_ndim, perm, &x_axes, &out_axes);
// Step2: Sharding Propagation
// Step2.1: Merge input shardings
std::pair<std::string, std::vector<std::vector<int64_t>>> x_sharding_info(
{x_axes, x_dims_mapping});
const auto& axes_size = GetAxesSizes({{x_axes, x_shape}});
const auto& mesh_shape = x_dist_attr_src.process_mesh().shape();
std::unordered_map<std::string, std::vector<int64_t>> axis_to_dim_map =
ShardingMergeForTensors({x_sharding_info}, axes_size, mesh_shape);
// Step2.2: Infer output dims mapping from merged input dims mapping
std::vector<std::vector<int64_t>> out_dims_mapping =
GetDimsMappingForAxes(out_axes, axis_to_dim_map);
auto x_dist_attr_dst = CopyTensorDistAttrForOutput(x_dist_attr_src);
x_dist_attr_dst.set_partial_status(x_dist_attr_src.partial_status());
x_dist_attr_dst.set_dims_mapping(x_dims_mapping);
// initialize output dist_attr's process_mesh, batch_dim and dynamic dims with
// input dist_attr.
TensorDistAttr out_dist_attr = CopyTensorDistAttrForOutput(x_dist_attr_src);
out_dist_attr.set_dims_mapping(out_dims_mapping);
out_dist_attr.set_partial_status(x_dist_attr_dst.partial_status());
VLOG(4) << "TransposeInferSpmd:";
VLOG(4) << "Input: shape: [" << str_join(x_shape) << "] "
<< "src_dims_mapping: [" << str_join(x_dims_mapping) << "] "
<< "dst_dims_mapping: [" << str_join(x_dims_mapping) << "]";
VLOG(4) << "Perm: [" << str_join(perm) << "]";
VLOG(4) << "Output dims_mapping: [" + str_join(out_dims_mapping) + "]\n\n";
return {{x_dist_attr_dst}, {out_dist_attr}};
}
SpmdInfo TransposeInferSpmdReverse(const DistMetaTensor& x,
const DistMetaTensor& out,
const std::vector<int>& perm) {
// Step0: Verify input args based on transpose logic
const std::vector<int64_t> x_shape = vectorize(x.dims());
const std::vector<int64_t> out_shape = vectorize(out.dims());
int x_ndim = static_cast<int>(x_shape.size());
int out_ndim = static_cast<int>(out_shape.size());
TensorDistAttr out_dist_attr_src = out.dist_attr();
std::vector<std::vector<int64_t>> out_dims_mapping =
out_dist_attr_src.multi_dims_mapping();
PADDLE_ENFORCE_EQ(
out_ndim,
out_dims_mapping.size(),
common::errors::InvalidArgument("The Tensor Out's rank [%d] and Out's "
"dims_mapping size [%d] are not matched.",
out_ndim,
out_dims_mapping.size()));
PADDLE_ENFORCE_EQ(
x_ndim,
out_ndim,
common::errors::InvalidArgument("The Tensor X's rank [%d] and "
"Out's rank [%d] are not matched.",
x_ndim,
out_ndim));
// check perm size
PADDLE_ENFORCE_EQ(
out_ndim,
perm.size(),
common::errors::InvalidArgument("The Tensor Out's rank [%d] and "
"perm size [%d] are not matched.",
out_ndim,
perm.size()));
// Step1: Build Einsum Notation
std::string x_axes;
std::string out_axes;
BuildEinsumNotation(x_ndim, perm, &x_axes, &out_axes);
// Step2: Sharding Propagation
// Step2.1: merge input shardings
std::pair<std::string, std::vector<std::vector<int64_t>>> out_sharding_info(
{out_axes, out_dims_mapping});
const auto& axes_size = GetAxesSizes({{out_axes, out_shape}});
const auto& mesh_shape = out_dist_attr_src.process_mesh().shape();
std::unordered_map<std::string, std::vector<int64_t>> axis_to_dim_map =
ShardingMergeForTensors({out_sharding_info}, axes_size, mesh_shape);
// step2.2: infer input dims mapping from merged output dims mapping
std::vector<std::vector<int64_t>> x_dims_mapping =
GetDimsMappingForAxes(x_axes, axis_to_dim_map);
// initialize output dist_attr's process_mesh, batch_dim and dynamic dims with
// input dist_attr.
TensorDistAttr x_dist_attr = CopyTensorDistAttrForOutput(x.dist_attr());
x_dist_attr.set_dims_mapping(x_dims_mapping);
auto out_dist_attr_dst = CopyTensorDistAttrForOutput(out_dist_attr_src);
out_dist_attr_dst.set_dims_mapping(out_dims_mapping);
// Step3 Handle partial (TODO)
VLOG(4) << "TransposeInferSpmdReverse:";
VLOG(4) << "Output shape: [" << str_join(out_shape) << "] "
<< "dims_mapping: [" << str_join(out_dims_mapping) << "]";
VLOG(4) << "Perm: [" << str_join(perm) << "]";
VLOG(4) << "Input shape: [" << str_join(x_shape) << "] "
<< "dims_mapping: [" << str_join(x_dims_mapping) << "]\n\n";
return {{x_dist_attr}, {out_dist_attr_dst}};
}
SpmdInfo TransposeGradInferSpmd(const DistMetaTensor& out_grad,
const std::vector<int>& perm) {
const std::vector<int64_t> out_grad_shape = vectorize(out_grad.dims());
size_t out_grad_ndim = out_grad_shape.size();
const std::vector<std::vector<int64_t>> out_grad_dims_mapping =
out_grad.dist_attr().multi_dims_mapping();
size_t out_grad_dims_mapping_size = out_grad_dims_mapping.size();
PADDLE_ENFORCE_EQ(out_grad_ndim,
out_grad_dims_mapping_size,
common::errors::InvalidArgument(
"The Tensor Out_grad's rank [%d] and "
"Out_grad's dims_mapping size [%d] are not matched.",
out_grad_ndim,
out_grad_dims_mapping_size));
size_t perm_size = perm.size();
PADDLE_ENFORCE_EQ(out_grad_ndim,
perm_size,
common::errors::InvalidArgument(
"The Tensor Out_grad's rank [%d] and perm size "
"[%d] are not matched.",
out_grad_ndim,
perm_size));
std::vector<std::vector<int64_t>> x_dims_mapping(out_grad_ndim,
std::vector<int64_t>({}));
for (size_t i = 0; i < perm.size(); ++i) {
int origin_index = perm[i] >= 0 ? perm[i] : out_grad_ndim + perm[i];
x_dims_mapping[origin_index] = out_grad_dims_mapping[i];
}
auto out_grad_dist_attr = CopyTensorDistAttrForOutput(out_grad.dist_attr());
out_grad_dist_attr.set_dims_mapping(out_grad_dims_mapping);
auto x_grad_dist_attr = CopyTensorDistAttrForOutput(out_grad.dist_attr());
x_grad_dist_attr.set_dims_mapping(x_dims_mapping);
return {{out_grad_dist_attr}, {x_grad_dist_attr}};
}
} // namespace phi::distributed