// 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. #include #include #include #include #include #include "ATen/ATen.h" #include "gtest/gtest.h" #include "test/cpp/prim/init_env_utils.h" #include "torch/all.h" namespace { class TensorExpandTest : public ::testing::Test { protected: static void SetUpTestSuite() { paddle::prim::InitTensorOperants(); } }; } // namespace // ======================== expand tests ======================== TEST_F(TensorExpandTest, ExpandBasic) { // {3}.expand({3,4}) - PyTorch rejects non-singleton expansion (3 != 4) at::Tensor t = at::arange(3, at::kFloat); ASSERT_THROW(t.expand({3, 4}), std::exception); } TEST_F(TensorExpandTest, ExpandSingleDim) { at::Tensor t = at::full({1}, 5.0f, at::kFloat); at::Tensor result = t.expand({5}); ASSERT_EQ(result.numel(), 5); } TEST_F(TensorExpandTest, ExpandMultipleDims) { // {1,3}.expand({2,3,4}) - PyTorch rejects non-singleton expansion (3 != 4) at::Tensor t = at::full({1, 3}, 1.0f, at::kFloat); ASSERT_THROW(t.expand({2, 3, 4}), std::exception); } TEST_F(TensorExpandTest, ExpandWithImplicit) { // {3}.expand({3,4}) - PyTorch rejects non-singleton expansion (3 != 4) at::Tensor t = at::arange(3, at::kFloat); ASSERT_THROW(t.expand({3, 4}, true), std::exception); } TEST_F(TensorExpandTest, ExpandPreservesValue) { // {3}.expand({3,4}) - PyTorch rejects non-singleton expansion (3 != 4) at::Tensor t = at::full({3}, 7.0f, at::kFloat); ASSERT_THROW(t.expand({3, 4}), std::exception); } // ======================== expand_as tests ======================== TEST_F(TensorExpandTest, ExpandAsBasic) { at::Tensor t = at::arange(3, at::kFloat).reshape({1, 3}); at::Tensor other = at::zeros({2, 3}, at::kFloat); at::Tensor result = t.expand_as(other); ASSERT_EQ(result.sizes()[0], 2); ASSERT_EQ(result.sizes()[1], 3); } TEST_F(TensorExpandTest, ExpandAsMatchSize) { at::Tensor t = at::full({1}, 7.0f, at::kFloat); at::Tensor other = at::zeros({3, 3, 3}, at::kFloat); at::Tensor result = t.expand_as(other); ASSERT_EQ(result.sizes().size(), 3); ASSERT_EQ(result.numel(), other.numel()); } TEST_F(TensorExpandTest, ExpandAsPreservesValue) { at::Tensor t = at::full({2, 1}, 5.0f, at::kFloat); at::Tensor other = at::zeros({2, 3}, at::kFloat); at::Tensor result = t.expand_as(other); ASSERT_FLOAT_EQ(result.data_ptr()[0], 5.0f); } // ======================== Additional tests for coverage // ======================== // Test tile fallback path when input_rank < target_rank // This triggers lines 86-100 in expand.h TEST_F(TensorExpandTest, ExpandTileFallbackLowRank) { // {2,1}.expand({1,4}) - PyTorch rejects shrinking non-singleton dims at::Tensor t = at::full({2, 1}, 1.0f, at::kFloat); ASSERT_THROW(t.expand({1, 4}), std::exception); } // Test tile fallback when input_rank == target_rank // This triggers lines 119-130 in expand.h TEST_F(TensorExpandTest, ExpandSameRankTileFallback) { // {2,3}.expand({2,6}) - PyTorch only allows expanding singleton dims at::Tensor t = at::full({2, 3}, 2.0f, at::kFloat); ASSERT_THROW(t.expand({2, 6}), std::exception); } // Test zero dimension handling // This triggers lines 90-94 and 122-126 in expand.h TEST_F(TensorExpandTest, ExpandZeroDim) { // {0}.expand({0,3}) - PyTorch rejects non-singleton expansion (0 != 3) at::Tensor t = at::full({0}, 1.0f, at::kFloat); ASSERT_THROW(t.expand({0, 3}), std::exception); } // Test input_rank > target_rank branch // This triggers lines 131-136 in expand.h TEST_F(TensorExpandTest, ExpandHighRankToLowRank) { // Input has more dimensions than target - PyTorch rejects this at::Tensor t = at::full({2, 3, 4}, 1.0f, at::kFloat); ASSERT_THROW(t.expand({3, 4}), std::exception); } // Test expand_as with tile fallback TEST_F(TensorExpandTest, ExpandAsTileFallback) { // {2,1}.expand_as({1,4}) - PyTorch rejects shrinking non-singleton dims at::Tensor t = at::full({2, 1}, 3.0f, at::kFloat); at::Tensor other = at::zeros({1, 4}, at::kFloat); ASSERT_THROW(t.expand_as(other), std::exception); } // Test preserve non-singleton dimension (matching dimension) TEST_F(TensorExpandTest, ExpandPreserveNonSingleton) { // {3,1}.expand({3,4}) - dim 0 matches (3), dim 1 expands (1->4) at::Tensor t = at::full({3, 1}, 5.0f, at::kFloat); at::Tensor result = t.expand({3, 4}); ASSERT_EQ(result.sizes()[0], 3); ASSERT_EQ(result.sizes()[1], 4); ASSERT_FLOAT_EQ(result.data_ptr()[0], 5.0f); ASSERT_FLOAT_EQ(result.data_ptr()[3], 5.0f); } // Test expand function (not member function) TEST_F(TensorExpandTest, ExpandFunction) { at::Tensor t = at::full({1}, 7.0f, at::kFloat); at::Tensor result = at::expand(t, {3, 4}); ASSERT_EQ(result.sizes()[0], 3); ASSERT_EQ(result.sizes()[1], 4); ASSERT_FLOAT_EQ(result.data_ptr()[0], 7.0f); } TEST_F(TensorExpandTest, ExpandAsMemberFunction) { at::Tensor t = at::full({1, 2}, 4.0f, at::kFloat); at::Tensor other = at::zeros({3, 2}, at::kFloat); at::Tensor result = t.expand_as(other); ASSERT_EQ(result.sizes()[0], 3); ASSERT_EQ(result.sizes()[1], 2); ASSERT_FLOAT_EQ(result.data_ptr()[0], 4.0f); }