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

203 lines
7.1 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.
#include <ATen/Functions.h>
#include <ATen/core/TensorBody.h>
#include <ATen/cuda/EmptyTensor.h>
#include <ATen/native/cuda/Resize.h>
#include <ATen/ops/tensor.h>
#include <c10/core/ScalarType.h>
#include <c10/core/SymInt.h>
#include <c10/core/TensorOptions.h>
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
#include <c10/cuda/CUDAFunctions.h>
#include <c10/cuda/CUDAGuard.h>
#endif
#include "ATen/ATen.h"
#include "gtest/gtest.h"
#include "paddle/phi/common/float16.h"
#include "torch/all.h"
// ============================================================================
// Flatten Tests
// ============================================================================
TEST(TestFlatten, FlattenAllDims) {
// Test flatten with start_dim=0, end_dim=-1
// Flattens the entire tensor to 1D
at::Tensor tensor = at::ones({2, 3, 4}, at::kFloat);
at::Tensor flattened = tensor.flatten(0, -1);
ASSERT_EQ(flattened.sizes(), c10::IntArrayRef({24}));
ASSERT_EQ(flattened.numel(), tensor.numel());
}
TEST(TestFlatten, FlattenPartialDims) {
// Test flatten with specific start and end dimensions
at::Tensor tensor = at::ones({2, 3, 4, 5}, at::kFloat);
// Flatten dimensions 1 to 2 (3*4 = 12)
at::Tensor flattened = tensor.flatten(1, 2);
ASSERT_EQ(flattened.sizes(), c10::IntArrayRef({2, 12, 5}));
ASSERT_EQ(flattened.numel(), tensor.numel());
}
TEST(TestFlatten, FlattenSingleDim) {
// Test flatten when start_dim == end_dim (should be no-op)
at::Tensor tensor = at::ones({2, 3, 4}, at::kFloat);
at::Tensor flattened = tensor.flatten(1, 1);
ASSERT_EQ(flattened.sizes(), c10::IntArrayRef({2, 3, 4}));
}
TEST(TestFlatten, FlattenNegativeDims) {
// Test flatten with negative dimension indices
at::Tensor tensor = at::ones({2, 3, 4, 5}, at::kFloat);
// Flatten from -3 to -2 (dimensions 1 to 2)
at::Tensor flattened = tensor.flatten(-3, -2);
ASSERT_EQ(flattened.sizes(), c10::IntArrayRef({2, 12, 5}));
}
TEST(TestFlatten, FlattenFirstTwoDims) {
// Test flatten on first two dimensions
at::Tensor tensor = at::ones({2, 3, 4}, at::kFloat);
at::Tensor flattened = tensor.flatten(0, 1);
ASSERT_EQ(flattened.sizes(), c10::IntArrayRef({6, 4}));
}
TEST(TestFlatten, FlattenLastTwoDims) {
// Test flatten on last two dimensions
at::Tensor tensor = at::ones({2, 3, 4}, at::kFloat);
at::Tensor flattened = tensor.flatten(1, 2);
ASSERT_EQ(flattened.sizes(), c10::IntArrayRef({2, 12}));
}
TEST(TestFlatten, FlattenDataIntegrity) {
// Test that flatten preserves data
at::Tensor tensor = at::arange(24, at::kFloat).reshape({2, 3, 4});
at::Tensor flattened = tensor.flatten(0, -1);
const float* original_data = tensor.data_ptr<float>();
const float* flattened_data = flattened.data_ptr<float>();
for (int64_t i = 0; i < tensor.numel(); ++i) {
ASSERT_EQ(original_data[i], flattened_data[i]);
}
}
// ============================================================================
// Unflatten Tests
// ============================================================================
TEST(TestUnflatten, UnflattenBasic) {
// Test basic unflatten operation
at::Tensor tensor = at::ones({4, 6, 8}, at::kFloat);
// Unflatten dimension 1 (size 6) into (2, 3)
at::Tensor unflattened = tensor.unflatten(1, c10::IntArrayRef({2, 3}));
ASSERT_EQ(unflattened.sizes(), c10::IntArrayRef({4, 2, 3, 8}));
ASSERT_EQ(unflattened.numel(), tensor.numel());
}
TEST(TestUnflatten, UnflattenFirstDim) {
// Test unflatten on first dimension
at::Tensor tensor = at::ones({6, 4}, at::kFloat);
// Unflatten dimension 0 (size 6) into (2, 3)
at::Tensor unflattened = tensor.unflatten(0, c10::IntArrayRef({2, 3}));
ASSERT_EQ(unflattened.sizes(), c10::IntArrayRef({2, 3, 4}));
}
TEST(TestUnflatten, UnflattenLastDim) {
// Test unflatten on last dimension
at::Tensor tensor = at::ones({2, 12}, at::kFloat);
// Unflatten dimension 1 (size 12) into (3, 4)
at::Tensor unflattened = tensor.unflatten(1, c10::IntArrayRef({3, 4}));
ASSERT_EQ(unflattened.sizes(), c10::IntArrayRef({2, 3, 4}));
}
TEST(TestUnflatten, UnflattenNegativeDim) {
// Test unflatten with negative dimension index
at::Tensor tensor = at::ones({4, 6, 8}, at::kFloat);
// Unflatten dimension -1 (last dim, size 8) into (4, 2)
at::Tensor unflattened = tensor.unflatten(-1, c10::IntArrayRef({4, 2}));
ASSERT_EQ(unflattened.sizes(), c10::IntArrayRef({4, 6, 4, 2}));
}
TEST(TestUnflatten, UnflattenSymInt) {
// Test unflatten_symint (should behave same as unflatten)
at::Tensor tensor = at::ones({4, 6, 8}, at::kFloat);
// Unflatten dimension 1 using symint version
// Note: Must keep the underlying data alive
std::vector<c10::SymInt> sizes_vec = {2, 3};
c10::SymIntArrayRef sizes(sizes_vec);
at::Tensor unflattened = tensor.unflatten_symint(1, sizes);
ASSERT_EQ(unflattened.sizes(), c10::IntArrayRef({4, 2, 3, 8}));
}
TEST(TestUnflatten, UnflattenDataIntegrity) {
// Test that unflatten preserves data
at::Tensor tensor = at::arange(24, at::kFloat).reshape({2, 12});
at::Tensor unflattened = tensor.unflatten(1, c10::IntArrayRef({3, 4}));
// Verify shape
ASSERT_EQ(unflattened.sizes(), c10::IntArrayRef({2, 3, 4}));
// Verify numel
ASSERT_EQ(unflattened.numel(), tensor.numel());
}
// ============================================================================
// Flatten and Unflatten Combined Tests
// ============================================================================
TEST(TestFlattenUnflatten, RoundTrip) {
// Test that flatten followed by unflatten restores original shape
at::Tensor tensor = at::arange(24, at::kFloat).reshape({2, 3, 4});
// Flatten dimensions 1 and 2
at::Tensor flattened = tensor.flatten(1, 2);
ASSERT_EQ(flattened.sizes(), c10::IntArrayRef({2, 12}));
// Unflatten back to original shape
at::Tensor unflattened = flattened.unflatten(1, c10::IntArrayRef({3, 4}));
ASSERT_EQ(unflattened.sizes(), c10::IntArrayRef({2, 3, 4}));
// Verify data integrity
ASSERT_EQ(tensor.numel(), unflattened.numel());
}
TEST(TestFlattenUnflatten, MultipleOperations) {
// Test multiple flatten/unflatten operations
at::Tensor tensor = at::ones({2, 3, 4, 5}, at::kFloat);
// Flatten all dimensions
at::Tensor flattened = tensor.flatten(0, -1);
ASSERT_EQ(flattened.sizes(), c10::IntArrayRef({120}));
// Unflatten into different shape
at::Tensor unflattened = flattened.unflatten(0, c10::IntArrayRef({6, 20}));
ASSERT_EQ(unflattened.sizes(), c10::IntArrayRef({6, 20}));
// Unflatten again
at::Tensor final_tensor = unflattened.unflatten(1, c10::IntArrayRef({4, 5}));
ASSERT_EQ(final_tensor.sizes(), c10::IntArrayRef({6, 4, 5}));
}