Files
2026-07-13 12:40:42 +08:00

138 lines
4.7 KiB
C++

// Copyright (c) 2026 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 <ATen/core/Tensor.h>
#include "paddle/phi/api/include/api.h"
namespace at {
// expand - expands tensor to new size
// PyTorch's expand works by right-aligning dimensions and broadcasting
// dimensions with size 1 to the target size
// Unlike Paddle's expand_v2, PyTorch allows non-singleton dimensions to be
// preserved when they match the corresponding target dimension
inline Tensor expand(const Tensor& self,
at::IntArrayRef size,
bool implicit = false) {
// implicit parameter is used by PyTorch's vmap for internal optimization.
// It doesn't affect the actual expand operation, so we can safely ignore it.
paddle::Tensor pd_tensor = self._PD_GetInner();
// Target sizes - convert to vector
std::vector<int64_t> target_size_vec(size.begin(), size.end());
auto target_rank = target_size_vec.size();
auto input_dims = pd_tensor.dims();
auto input_rank = static_cast<size_t>(input_dims.size());
// PyTorch's expand uses right-alignment semantics:
// - For 1D tensor expand to 2D: {3}.expand({3,4}) treats input as {3,1},
// expands to {3,4}
// - Non-singleton dimensions are preserved, singleton dimensions (1) can
// expand
//
// For example:
// {3}.expand({3, 4}) -> input {3} becomes {3, 1} implicitly
// then expand: dim 0: 3 stays 3, dim 1: 1 -> 4 -> result {3, 4}
if (input_rank < target_rank) {
// Add leading 1s to right-align with target shape (PyTorch behavior)
// Input {1, 2}, target {2, 3, 2} -> reshape to {1, 1, 2}
std::vector<int64_t> reshape_vec(target_rank, 1);
for (size_t i = 0; i < input_rank; ++i) {
reshape_vec[target_rank - input_rank + i] = input_dims[i];
}
// Check if Paddle's expand can handle this right-aligned shape
// Paddle allows: input[i] == 1 (can expand), or input[i] == target[i]
// (match)
bool can_use_paddle_expand = true;
size_t fail_dim = 0;
for (size_t i = 0; i < target_rank; ++i) {
bool dim_can_expand = (reshape_vec[i] == 1);
bool dim_is_matching = (reshape_vec[i] == target_size_vec[i]);
if (!dim_can_expand && !dim_is_matching) {
can_use_paddle_expand = false;
fail_dim = i;
break;
}
}
if (can_use_paddle_expand) {
// Reshape to right-aligned shape, then expand
paddle::Tensor reshaped =
paddle::experimental::reshape(pd_tensor, phi::IntArray(reshape_vec));
paddle::Tensor result = paddle::experimental::expand(
reshaped, phi::IntArray(target_size_vec));
return Tensor(result);
}
PD_THROW("expand(): the expanded size of the tensor (",
target_size_vec[fail_dim],
") must match the existing size (",
reshape_vec[fail_dim],
") at non-singleton dimension ",
fail_dim,
".");
} else if (input_rank == target_rank) {
// Same rank - check if we can use expand directly
bool can_use_paddle_expand = true;
size_t fail_dim = 0;
for (size_t i = 0; i < target_rank; ++i) {
auto in_size = input_dims[i];
auto target_size = target_size_vec[i];
if (in_size != 1 && in_size != target_size) {
can_use_paddle_expand = false;
fail_dim = i;
break;
}
}
if (can_use_paddle_expand) {
paddle::Tensor result = paddle::experimental::expand(
pd_tensor, phi::IntArray(target_size_vec));
return Tensor(result);
}
PD_THROW("expand(): the expanded size of the tensor (",
target_size_vec[fail_dim],
") must match the existing size (",
input_dims[fail_dim],
") at non-singleton dimension ",
fail_dim,
".");
} else {
PD_THROW("expand(): the number of sizes provided (",
target_rank,
") must be greater or equal to the number of dimensions in the "
"tensor (",
input_rank,
").");
}
}
} // namespace at
namespace at {
// Member function: Tensor::expand
inline Tensor Tensor::expand(at::IntArrayRef size, bool implicit) const {
return at::expand(*this, size, implicit);
}
} // namespace at