diff --git a/.claude/skills/add-compat-api/SKILL.md b/.claude/skills/add-compat-api/SKILL.md index 1bc79e5..b784e5e 100644 --- a/.claude/skills/add-compat-api/SKILL.md +++ b/.claude/skills/add-compat-api/SKILL.md @@ -202,12 +202,12 @@ bash test/result_cmp.sh ./build/ 闭环验证通过且文档已回填后,按以下流程提交。完整命令模板见 [`references/Step7.md`](references/Step7.md)。 -1. **从 fork 主分支 checkout 新分支**(`git fetch upstream && git checkout -B add/- upstream/develop`) +1. **从本地跟踪 origin 的 develop 创建新分支**(`git checkout develop && git pull --ff-only origin develop && git checkout -b add/-`) 2. **commit 改动**(commit message 首行使用 `[Cpp API Compatibility] <对齐迭代记录标题>`) 3. **push 到 fork**(`git push origin `) 4. **`gh pr create` 到 upstream**(`--repo PaddlePaddle/Paddle --base develop`) 5. **同步 PCAT 测试改动** - 1. **从 fork 主分支 checkout 新分支**(`cd "$PCAT_ROOT" && git fetch upstream && git checkout -B test/- upstream/master`) + 1. **从本地跟踪 origin 的 master 创建新分支**(`cd "$PCAT_ROOT" && git checkout master && git pull --ff-only origin master && git checkout -b test/-`) 2. **commit 改动**(commit message 首行使用 `test(): align with Paddle compat 行为`) 3. **push 到 fork**(`git push origin `) 4. **`gh pr create` 到 upstream**(`--repo PFCCLab/PaddleCppAPITest --base master`,PR body 中加 `Related: PaddlePaddle/Paddle#`) diff --git a/.claude/skills/add-compat-api/references/Step7.md b/.claude/skills/add-compat-api/references/Step7.md index 892239d..ec2f5d4 100644 --- a/.claude/skills/add-compat-api/references/Step7.md +++ b/.claude/skills/add-compat-api/references/Step7.md @@ -13,24 +13,27 @@ 任一未通过 → **不要**进入本流程。 -## 1) 从 fork 主分支 checkout 新分支 +## 1) 从本地跟踪 origin 的基分支创建新分支 ### Paddle 侧 ```bash cd "$PADDLE_ROOT" -git fetch upstream +git checkout develop +git pull --ff-only origin develop # add 流程 -git checkout -B "add/-$(date +%Y%m%d)" upstream/develop +git checkout -b "add/-$(date +%Y%m%d)" # fix 流程 -git checkout -B "fix/-$(date +%Y%m%d)" upstream/develop +git checkout -b "fix/-$(date +%Y%m%d)" ``` 分支命名规则: - add:`add/-`(例:`add/abs-20260519`) - fix:`fix/-`(例:`fix/78652-20260519`) -> 前提:Paddle 仓库已配置 `origin = /Paddle` 与 `upstream = PaddlePaddle/Paddle`。若 remote 缺失,**提示用户配置后再继续**,不要主动改用户仓库的 remote。 +> 实际执行时只选 add 或 fix 对应的一条 `git checkout -b`。若分支已存在,`git checkout -b` 会失败;换新分支名或让用户手动处理,**不要**改用 `-B` 覆盖已有分支。 +> +> 前提:Paddle 仓库已配置 `origin = /Paddle` 与 `upstream = PaddlePaddle/Paddle`,且本地 `develop` 跟踪 `origin/develop`。若 remote 或本地基分支缺失,**提示用户配置后再继续**,不要主动改用户仓库的 remote。 ## 2) commit 改动 @@ -100,8 +103,9 @@ EOF ```bash cd "$PCAT_ROOT" -git fetch upstream -git checkout -B "test/-$(date +%Y%m%d)" upstream/master +git checkout master +git pull --ff-only origin master +git checkout -b "test/-$(date +%Y%m%d)" git add test/<改动文件> git commit -m "test(): align with Paddle compat 行为" git push origin diff --git a/.claude/skills/fix-compat-api/SKILL.md b/.claude/skills/fix-compat-api/SKILL.md index 9245cf8..d366845 100644 --- a/.claude/skills/fix-compat-api/SKILL.md +++ b/.claude/skills/fix-compat-api/SKILL.md @@ -218,7 +218,7 @@ bash test/result_cmp.sh ./build/ 闭环验证通过且文档已回填后,按以下流程提交。完整命令模板见 [`../add-compat-api/references/Step7.md`](../add-compat-api/references/Step7.md)(与 `add-compat-api` 共享同一份 references)。 -1. **从 fork 主分支 checkout 新分支**(`git fetch upstream && git checkout -B fix/- upstream/develop`) +1. **从本地跟踪 origin 的 develop 创建新分支**(`git checkout develop && git pull --ff-only origin develop && git checkout -b fix/-`) 2. **commit 改动**(commit message 首行使用 `[Cpp API Compatibility] `) 3. **征求用户同意后 push 到 fork**(`git push origin `——这是发出去的动作,**push 前必须明确询问用户**) 4. **征求用户同意后 `gh pr create` 到 upstream**(`--repo PaddlePaddle/Paddle --base develop`——同样需要用户确认;若本轮修复源自外部 PR/Actions/comment 链接,PR 描述里应**引用原链接**便于追溯) diff --git a/.github/skills/add-compat-api/SKILL.md b/.github/skills/add-compat-api/SKILL.md index 1bc79e5..b784e5e 100644 --- a/.github/skills/add-compat-api/SKILL.md +++ b/.github/skills/add-compat-api/SKILL.md @@ -202,12 +202,12 @@ bash test/result_cmp.sh ./build/ 闭环验证通过且文档已回填后,按以下流程提交。完整命令模板见 [`references/Step7.md`](references/Step7.md)。 -1. **从 fork 主分支 checkout 新分支**(`git fetch upstream && git checkout -B add/- upstream/develop`) +1. **从本地跟踪 origin 的 develop 创建新分支**(`git checkout develop && git pull --ff-only origin develop && git checkout -b add/-`) 2. **commit 改动**(commit message 首行使用 `[Cpp API Compatibility] <对齐迭代记录标题>`) 3. **push 到 fork**(`git push origin `) 4. **`gh pr create` 到 upstream**(`--repo PaddlePaddle/Paddle --base develop`) 5. **同步 PCAT 测试改动** - 1. **从 fork 主分支 checkout 新分支**(`cd "$PCAT_ROOT" && git fetch upstream && git checkout -B test/- upstream/master`) + 1. **从本地跟踪 origin 的 master 创建新分支**(`cd "$PCAT_ROOT" && git checkout master && git pull --ff-only origin master && git checkout -b test/-`) 2. **commit 改动**(commit message 首行使用 `test(): align with Paddle compat 行为`) 3. **push 到 fork**(`git push origin `) 4. **`gh pr create` 到 upstream**(`--repo PFCCLab/PaddleCppAPITest --base master`,PR body 中加 `Related: PaddlePaddle/Paddle#`) diff --git a/.github/skills/add-compat-api/references/Step7.md b/.github/skills/add-compat-api/references/Step7.md index 892239d..ec2f5d4 100644 --- a/.github/skills/add-compat-api/references/Step7.md +++ b/.github/skills/add-compat-api/references/Step7.md @@ -13,24 +13,27 @@ 任一未通过 → **不要**进入本流程。 -## 1) 从 fork 主分支 checkout 新分支 +## 1) 从本地跟踪 origin 的基分支创建新分支 ### Paddle 侧 ```bash cd "$PADDLE_ROOT" -git fetch upstream +git checkout develop +git pull --ff-only origin develop # add 流程 -git checkout -B "add/-$(date +%Y%m%d)" upstream/develop +git checkout -b "add/-$(date +%Y%m%d)" # fix 流程 -git checkout -B "fix/-$(date +%Y%m%d)" upstream/develop +git checkout -b "fix/-$(date +%Y%m%d)" ``` 分支命名规则: - add:`add/-`(例:`add/abs-20260519`) - fix:`fix/-`(例:`fix/78652-20260519`) -> 前提:Paddle 仓库已配置 `origin = /Paddle` 与 `upstream = PaddlePaddle/Paddle`。若 remote 缺失,**提示用户配置后再继续**,不要主动改用户仓库的 remote。 +> 实际执行时只选 add 或 fix 对应的一条 `git checkout -b`。若分支已存在,`git checkout -b` 会失败;换新分支名或让用户手动处理,**不要**改用 `-B` 覆盖已有分支。 +> +> 前提:Paddle 仓库已配置 `origin = /Paddle` 与 `upstream = PaddlePaddle/Paddle`,且本地 `develop` 跟踪 `origin/develop`。若 remote 或本地基分支缺失,**提示用户配置后再继续**,不要主动改用户仓库的 remote。 ## 2) commit 改动 @@ -100,8 +103,9 @@ EOF ```bash cd "$PCAT_ROOT" -git fetch upstream -git checkout -B "test/-$(date +%Y%m%d)" upstream/master +git checkout master +git pull --ff-only origin master +git checkout -b "test/-$(date +%Y%m%d)" git add test/<改动文件> git commit -m "test(): align with Paddle compat 行为" git push origin diff --git a/.github/skills/fix-compat-api/SKILL.md b/.github/skills/fix-compat-api/SKILL.md index 9245cf8..d366845 100644 --- a/.github/skills/fix-compat-api/SKILL.md +++ b/.github/skills/fix-compat-api/SKILL.md @@ -218,7 +218,7 @@ bash test/result_cmp.sh ./build/ 闭环验证通过且文档已回填后,按以下流程提交。完整命令模板见 [`../add-compat-api/references/Step7.md`](../add-compat-api/references/Step7.md)(与 `add-compat-api` 共享同一份 references)。 -1. **从 fork 主分支 checkout 新分支**(`git fetch upstream && git checkout -B fix/- upstream/develop`) +1. **从本地跟踪 origin 的 develop 创建新分支**(`git checkout develop && git pull --ff-only origin develop && git checkout -b fix/-`) 2. **commit 改动**(commit message 首行使用 `[Cpp API Compatibility] `) 3. **征求用户同意后 push 到 fork**(`git push origin `——这是发出去的动作,**push 前必须明确询问用户**) 4. **征求用户同意后 `gh pr create` 到 upstream**(`--repo PaddlePaddle/Paddle --base develop`——同样需要用户确认;若本轮修复源自外部 PR/Actions/comment 链接,PR 描述里应**引用原链接**便于追溯) diff --git a/doc/ATen/core/tensor_body.md b/doc/ATen/core/tensor_body.md index f794fa1..c72ae90 100644 --- a/doc/ATen/core/tensor_body.md +++ b/doc/ATen/core/tensor_body.md @@ -75,7 +75,7 @@ | `ccol_indices` | ⏳ | ⏳ | P2 | | | `ceil` | ⏳ | ⏳ | P2 | | | `chalf` | ⏳ | ⏳ | P2 | | -| `cholesky` | ⏳ | ⏳ | P2 | | +| `cholesky` | ✅ | ✅ | P2 | Tensor 方法与 at::cholesky 均已实现 | | `cholesky_inverse` | ⏳ | ⏳ | P2 | | | `cholesky_solve` | ⏳ | ⏳ | P2 | | | `clamp` | ✅ | ✅ | P2 | 已实现 Scalar 和 Tensor 版本 | diff --git a/test/ATen/core/TensorUtilTest.cpp b/test/ATen/core/TensorUtilTest.cpp index f0c17c7..947b4bc 100644 --- a/test/ATen/core/TensorUtilTest.cpp +++ b/test/ATen/core/TensorUtilTest.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include "src/file_manager.h" @@ -17,6 +18,11 @@ namespace test { using paddle_api_test::FileManerger; using paddle_api_test::ThreadSafeParam; +static_assert(noexcept(std::declval().is_same( + std::declval()))); +static_assert(noexcept(std::declval().use_count())); +static_assert(noexcept(std::declval().weak_use_count())); + class TensorUtilTest : public ::testing::Test { protected: void SetUp() override { diff --git a/test/ATen/ops/AsStridedTest.cpp b/test/ATen/ops/AsStridedTest.cpp index 4410024..90d650e 100644 --- a/test/ATen/ops/AsStridedTest.cpp +++ b/test/ATen/ops/AsStridedTest.cpp @@ -19,19 +19,8 @@ class AsStridedTest : public ::testing::Test { void SetUp() override {} }; -// 返回当前用例的结果文件名 -std::string GetTestCaseResultFileName() { - std::string base = g_custom_param.get(); - std::string test_name = - ::testing::UnitTest::GetInstance()->current_test_info()->name(); - if (base.size() >= 4 && base.substr(base.size() - 4) == ".txt") { - base.resize(base.size() - 4); - } - return base + "_" + test_name + ".txt"; -} - TEST_F(AsStridedTest, AsStrided) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.createFile(); file << "AsStrided "; at::Tensor tensor = at::ones({2, 3, 4}, at::kFloat); @@ -44,7 +33,7 @@ TEST_F(AsStridedTest, AsStrided) { } TEST_F(AsStridedTest, AsStridedInplace) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "AsStridedInplace "; at::Tensor tensor = at::ones({2, 3, 4}, at::kFloat); @@ -57,7 +46,7 @@ TEST_F(AsStridedTest, AsStridedInplace) { } TEST_F(AsStridedTest, AsStridedScatter) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "AsStridedScatter "; at::Tensor tensor = at::ones({2, 3, 4}, at::kFloat); @@ -69,5 +58,40 @@ TEST_F(AsStridedTest, AsStridedScatter) { file.saveFile(); } +TEST_F(AsStridedTest, AsStridedScatterPreservesInputShape) { + FileManerger file(g_custom_param.get()); + file.openAppend(); + file << "AsStridedScatterPreservesInputShape "; + at::Tensor tensor = at::arange(12, at::kFloat); + at::Tensor src = at::full({2, 3}, 99.0f, at::kFloat); + at::Tensor result = tensor.as_strided_scatter(src, {2, 3}, {3, 1}); + file << std::to_string(result.dim()) << " "; + file << std::to_string(result.sizes()[0]) << " "; + float* data = result.data_ptr(); + for (int i = 0; i < 6; ++i) { + file << std::to_string(data[i]) << " "; + } + file << "\n"; + file.saveFile(); +} + +TEST_F(AsStridedTest, AsStridedScatterWithOffset) { + FileManerger file(g_custom_param.get()); + file.openAppend(); + file << "AsStridedScatterWithOffset "; + at::Tensor tensor = at::arange(12, at::kFloat); + at::Tensor src = at::full({2, 2}, 88.0f, at::kFloat); + at::Tensor result = tensor.as_strided_scatter(src, {2, 2}, {2, 1}, 2); + file << std::to_string(result.dim()) << " "; + file << std::to_string(result.sizes()[0]) << " "; + float* data = result.data_ptr(); + file << std::to_string(data[0]) << " "; + file << std::to_string(data[2]) << " "; + file << std::to_string(data[3]) << " "; + file << std::to_string(data[5]) << " "; + file << "\n"; + file.saveFile(); +} + } // namespace test } // namespace at diff --git a/test/ATen/ops/BitwiseTest.cpp b/test/ATen/ops/BitwiseTest.cpp index 90d2a55..77f2a85 100644 --- a/test/ATen/ops/BitwiseTest.cpp +++ b/test/ATen/ops/BitwiseTest.cpp @@ -19,19 +19,8 @@ class BitwiseTest : public ::testing::Test { void SetUp() override {} }; -// 返回当前用例的结果文件名 -std::string GetTestCaseResultFileName() { - std::string base = g_custom_param.get(); - std::string test_name = - ::testing::UnitTest::GetInstance()->current_test_info()->name(); - if (base.size() >= 4 && base.substr(base.size() - 4) == ".txt") { - base.resize(base.size() - 4); - } - return base + "_" + test_name + ".txt"; -} - TEST_F(BitwiseTest, BitwiseRightShift) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.createFile(); file << "BitwiseRightShift "; at::Tensor input = at::ones({2, 3}, at::kInt).fill_(8); diff --git a/test/ATen/ops/CholeskyTest.cpp b/test/ATen/ops/CholeskyTest.cpp new file mode 100644 index 0000000..bf17961 --- /dev/null +++ b/test/ATen/ops/CholeskyTest.cpp @@ -0,0 +1,385 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/file_manager.h" + +extern paddle_api_test::ThreadSafeParam g_custom_param; + +namespace at { +namespace test { + +using paddle_api_test::FileManerger; +using paddle_api_test::ThreadSafeParam; + +// 使用 stride 按逻辑行优先顺序写出 tensor 元素,兼容不同内存布局 +static void write_cholesky_result_to_file(FileManerger* file, + const at::Tensor& result) { + *file << std::to_string(result.dim()) << " "; + *file << std::to_string(result.numel()) << " "; + for (int64_t i = 0; i < result.dim(); ++i) { + *file << std::to_string(result.sizes()[i]) << " "; + } + + // 使用 stride 按逻辑顺序访问元素 + int64_t ndim = result.dim(); + std::vector strides(ndim); + for (int64_t i = 0; i < ndim; ++i) { + strides[i] = result.stride(i); + } + + switch (result.scalar_type()) { + case at::kFloat: { + float* data = result.data_ptr(); + if (ndim == 2) { + for (int64_t i = 0; i < result.size(0); ++i) { + for (int64_t j = 0; j < result.size(1); ++j) { + *file << std::to_string(data[i * strides[0] + j * strides[1]]) + << " "; + } + } + } else if (ndim == 3) { + for (int64_t b = 0; b < result.size(0); ++b) { + for (int64_t i = 0; i < result.size(1); ++i) { + for (int64_t j = 0; j < result.size(2); ++j) { + *file + << std::to_string( + data[b * strides[0] + i * strides[1] + j * strides[2]]) + << " "; + } + } + } + } + break; + } + case at::kDouble: { + double* data = result.data_ptr(); + if (ndim == 2) { + for (int64_t i = 0; i < result.size(0); ++i) { + for (int64_t j = 0; j < result.size(1); ++j) { + *file << std::to_string(data[i * strides[0] + j * strides[1]]) + << " "; + } + } + } else if (ndim == 3) { + for (int64_t b = 0; b < result.size(0); ++b) { + for (int64_t i = 0; i < result.size(1); ++i) { + for (int64_t j = 0; j < result.size(2); ++j) { + *file + << std::to_string( + data[b * strides[0] + i * strides[1] + j * strides[2]]) + << " "; + } + } + } + } + break; + } + default: { + *file << "unsupported_dtype "; + break; + } + } +} + +// 构建对角占优的对称正定矩阵 +static at::Tensor make_spd_matrix(const std::vector& shape, + at::ScalarType dtype) { + at::Tensor A = at::zeros(shape, dtype); + int64_t n = shape[shape.size() - 1]; + int64_t m = shape[shape.size() - 2]; + int64_t batch = 1; + for (size_t i = 0; i + 2 < shape.size(); ++i) { + batch *= shape[i]; + } + + if (dtype == at::kFloat) { + float* data = A.data_ptr(); + for (int64_t b = 0; b < batch; ++b) { + for (int64_t i = 0; i < m; ++i) { + for (int64_t j = 0; j < n; ++j) { + int64_t idx = b * m * n + i * n + j; + if (i == j) { + data[idx] = static_cast(n); + } else { + data[idx] = 0.5f; + } + } + } + } + } else if (dtype == at::kDouble) { + double* data = A.data_ptr(); + for (int64_t b = 0; b < batch; ++b) { + for (int64_t i = 0; i < m; ++i) { + for (int64_t j = 0; j < n; ++j) { + int64_t idx = b * m * n + i * n + j; + if (i == j) { + data[idx] = static_cast(n); + } else { + data[idx] = 0.5; + } + } + } + } + } + return A; +} + +template +static double max_reconstruction_error_data(const at::Tensor& factor, + const at::Tensor& original, + bool upper) { + const T* factor_data = factor.data_ptr(); + const T* original_data = original.data_ptr(); + const int64_t ndim = factor.dim(); + const int64_t n = factor.size(ndim - 1); + const int64_t batch = factor.numel() / (n * n); + std::vector factor_strides(ndim); + std::vector original_strides(ndim); + for (int64_t i = 0; i < ndim; ++i) { + factor_strides[i] = factor.stride(i); + original_strides[i] = original.stride(i); + } + + double max_error = 0.0; + for (int64_t b = 0; b < batch; ++b) { + const int64_t factor_batch_offset = ndim == 3 ? b * factor_strides[0] : 0; + const int64_t original_batch_offset = + ndim == 3 ? b * original_strides[0] : 0; + for (int64_t i = 0; i < n; ++i) { + for (int64_t j = 0; j < n; ++j) { + double reconstructed = 0.0; + for (int64_t k = 0; k < n; ++k) { + const int64_t left_idx = + factor_batch_offset + (upper ? k * factor_strides[ndim - 2] + + i * factor_strides[ndim - 1] + : i * factor_strides[ndim - 2] + + k * factor_strides[ndim - 1]); + const int64_t right_idx = + factor_batch_offset + (upper ? k * factor_strides[ndim - 2] + + j * factor_strides[ndim - 1] + : j * factor_strides[ndim - 2] + + k * factor_strides[ndim - 1]); + reconstructed += static_cast(factor_data[left_idx]) * + static_cast(factor_data[right_idx]); + } + const int64_t original_idx = original_batch_offset + + i * original_strides[ndim - 2] + + j * original_strides[ndim - 1]; + max_error = std::max( + max_error, + std::fabs(reconstructed - + static_cast(original_data[original_idx]))); + } + } + } + return max_error; +} + +static double max_reconstruction_error(const at::Tensor& factor, + const at::Tensor& original, + bool upper) { + if (factor.scalar_type() == at::kFloat) { + return max_reconstruction_error_data(factor, original, upper); + } + return max_reconstruction_error_data(factor, original, upper); +} + +static void write_reconstruction_error_to_file(FileManerger* file, + const at::Tensor& factor, + const at::Tensor& original, + bool upper) { + const double max_error = max_reconstruction_error(factor, original, upper); + *file << std::to_string(factor.dim()) << " "; + *file << std::to_string(factor.numel()) << " "; + for (int64_t i = 0; i < factor.dim(); ++i) { + *file << std::to_string(factor.sizes()[i]) << " "; + } + *file << std::to_string(max_error) << " "; +} + +class CholeskyTest : public ::testing::Test { + protected: + void SetUp() override {} +}; + +// ========== 基础功能 ========== + +TEST_F(CholeskyTest, BasicCholesky) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.createFile(); + file << "BasicCholesky "; + at::Tensor A = make_spd_matrix({3, 3}, at::kFloat); + at::Tensor result = at::cholesky(A); + write_cholesky_result_to_file(&file, result); + file << "\n"; + file.saveFile(); +} + +TEST_F(CholeskyTest, UpperTrue) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "UpperTrue "; + at::Tensor A = make_spd_matrix({3, 3}, at::kFloat); + at::Tensor result = at::cholesky(A, /*upper=*/true); + write_cholesky_result_to_file(&file, result); + file << "\n"; + file.saveFile(); +} + +// ========== Shape 覆盖 ========== + +// 小矩阵 {2, 2} +TEST_F(CholeskyTest, SmallMatrix) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "SmallMatrix "; + at::Tensor A = make_spd_matrix({2, 2}, at::kFloat); + at::Tensor result = at::cholesky(A); + write_cholesky_result_to_file(&file, result); + file << "\n"; + file.saveFile(); +} + +// 大矩阵 {8, 8} +TEST_F(CholeskyTest, LargeMatrix) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "LargeMatrix "; + at::Tensor A = make_spd_matrix({8, 8}, at::kFloat); + at::Tensor result = at::cholesky(A); + write_cholesky_result_to_file(&file, result); + file << "\n"; + file.saveFile(); +} + +// batch 矩阵 {2, 3, 3} +TEST_F(CholeskyTest, BatchMatrix) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "BatchMatrix "; + at::Tensor A = make_spd_matrix({2, 3, 3}, at::kFloat); + at::Tensor result = at::cholesky(A); + write_cholesky_result_to_file(&file, result); + file << "\n"; + file.saveFile(); +} + +TEST_F(CholeskyTest, LowerReconstructsInput) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "LowerReconstructsInput "; + at::Tensor A = make_spd_matrix({3, 3}, at::kFloat); + at::Tensor result = at::cholesky(A); + const double max_error = max_reconstruction_error(result, A, false); + EXPECT_LT(max_error, 1e-4); + write_reconstruction_error_to_file(&file, result, A, false); + file << "\n"; + file.saveFile(); +} + +TEST_F(CholeskyTest, UpperReconstructsInput) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "UpperReconstructsInput "; + at::Tensor A = make_spd_matrix({3, 3}, at::kFloat); + at::Tensor result = at::cholesky(A, /*upper=*/true); + const double max_error = max_reconstruction_error(result, A, true); + EXPECT_LT(max_error, 1e-4); + write_reconstruction_error_to_file(&file, result, A, true); + file << "\n"; + file.saveFile(); +} + +TEST_F(CholeskyTest, BatchReconstructsInput) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "BatchReconstructsInput "; + at::Tensor A = make_spd_matrix({2, 3, 3}, at::kFloat); + at::Tensor result = at::cholesky(A); + const double max_error = max_reconstruction_error(result, A, false); + EXPECT_LT(max_error, 1e-4); + write_reconstruction_error_to_file(&file, result, A, false); + file << "\n"; + file.saveFile(); +} + +// ========== Dtype 覆盖 ========== + +// float64 +TEST_F(CholeskyTest, Float64Dtype) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "Float64Dtype "; + at::Tensor A = make_spd_matrix({3, 3}, at::kDouble); + at::Tensor result = at::cholesky(A); + write_cholesky_result_to_file(&file, result); + file << "\n"; + file.saveFile(); +} + +// ========== API 变体 ========== + +// 方法调用 t.cholesky() +TEST_F(CholeskyTest, MethodCholesky) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "MethodCholesky "; + at::Tensor A = make_spd_matrix({3, 3}, at::kFloat); + at::Tensor result = A.cholesky(); + write_cholesky_result_to_file(&file, result); + file << "\n"; + file.saveFile(); +} + +// 方法调用 t.cholesky(true) +TEST_F(CholeskyTest, MethodCholeskyUpper) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "MethodCholeskyUpper "; + at::Tensor A = make_spd_matrix({3, 3}, at::kFloat); + at::Tensor result = A.cholesky(/*upper=*/true); + write_cholesky_result_to_file(&file, result); + file << "\n"; + file.saveFile(); +} + +// ========== 异常测试 ========== + +// 非正定矩阵应抛出异常 +TEST_F(CholeskyTest, NonPositiveDefinite) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "NonPositiveDefinite "; + at::Tensor A = at::zeros({3, 3}, at::kFloat); + try { + at::Tensor result = at::cholesky(A); + write_cholesky_result_to_file(&file, result); + } catch (const std::exception&) { + file << "exception "; + } + file << "\n"; + file.saveFile(); +} + +} // namespace test +} // namespace at diff --git a/test/ATen/ops/ClampTest.cpp b/test/ATen/ops/ClampTest.cpp index 26ed74d..28d54a3 100644 --- a/test/ATen/ops/ClampTest.cpp +++ b/test/ATen/ops/ClampTest.cpp @@ -19,19 +19,8 @@ class ClampTest : public ::testing::Test { void SetUp() override {} }; -// 返回当前用例的结果文件名 -std::string GetTestCaseResultFileName() { - std::string base = g_custom_param.get(); - std::string test_name = - ::testing::UnitTest::GetInstance()->current_test_info()->name(); - if (base.size() >= 4 && base.substr(base.size() - 4) == ".txt") { - base.resize(base.size() - 4); - } - return base + "_" + test_name + ".txt"; -} - TEST_F(ClampTest, ClampScalarMinMax) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.createFile(); file << "ClampScalarMinMax "; at::Tensor input = at::ones({2, 3}, at::kFloat).fill_(5.0f); @@ -44,7 +33,7 @@ TEST_F(ClampTest, ClampScalarMinMax) { } TEST_F(ClampTest, ClampTensorMinMax) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampTensorMinMax "; at::Tensor input = at::ones({2, 3}, at::kFloat).fill_(5.0f); @@ -57,7 +46,7 @@ TEST_F(ClampTest, ClampTensorMinMax) { } TEST_F(ClampTest, ClampInplaceScalar) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampInplaceScalar "; at::Tensor input = at::ones({2, 3}, at::kFloat).fill_(5.0f); @@ -69,7 +58,7 @@ TEST_F(ClampTest, ClampInplaceScalar) { } TEST_F(ClampTest, ClampInplaceTensor) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampInplaceTensor "; at::Tensor input = at::ones({2, 3}, at::kFloat).fill_(5.0f); @@ -82,7 +71,7 @@ TEST_F(ClampTest, ClampInplaceTensor) { } TEST_F(ClampTest, ClampMaxScalar) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampMaxScalar "; at::Tensor input = at::ones({2, 3}, at::kFloat).fill_(5.0f); @@ -94,7 +83,7 @@ TEST_F(ClampTest, ClampMaxScalar) { } TEST_F(ClampTest, ClampMaxTensor) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampMaxTensor "; at::Tensor input = at::ones({2, 3}, at::kFloat).fill_(5.0f); @@ -106,7 +95,7 @@ TEST_F(ClampTest, ClampMaxTensor) { } TEST_F(ClampTest, ClampMaxInplace) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampMaxInplace "; at::Tensor input = at::ones({2, 3}, at::kFloat).fill_(5.0f); @@ -118,7 +107,7 @@ TEST_F(ClampTest, ClampMaxInplace) { } TEST_F(ClampTest, ClampMaxInplaceTensor) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampMaxInplaceTensor "; at::Tensor input = at::ones({2, 3}, at::kFloat).fill_(5.0f); @@ -130,7 +119,7 @@ TEST_F(ClampTest, ClampMaxInplaceTensor) { } TEST_F(ClampTest, ClampMinScalar) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampMinScalar "; at::Tensor input = at::ones({2, 3}, at::kFloat); @@ -142,7 +131,7 @@ TEST_F(ClampTest, ClampMinScalar) { } TEST_F(ClampTest, ClampMinTensor) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampMinTensor "; at::Tensor input = at::ones({2, 3}, at::kFloat); @@ -154,7 +143,7 @@ TEST_F(ClampTest, ClampMinTensor) { } TEST_F(ClampTest, ClampMinInplace) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampMinInplace "; at::Tensor input = at::ones({2, 3}, at::kFloat); @@ -166,7 +155,7 @@ TEST_F(ClampTest, ClampMinInplace) { } TEST_F(ClampTest, ClampMinInplaceTensor) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "ClampMinInplaceTensor "; at::Tensor input = at::ones({2, 3}, at::kFloat); diff --git a/test/ATen/ops/IndexPutTest.cpp b/test/ATen/ops/IndexPutTest.cpp index 3b1b258..ba36b1d 100644 --- a/test/ATen/ops/IndexPutTest.cpp +++ b/test/ATen/ops/IndexPutTest.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -190,5 +191,62 @@ TEST(TensorBodyTest, IndexPutAccumulateTrue) { file.saveFile(); } +TEST(TensorBodyTest, IndexPutTensorIndexNoneValue) { + auto options = at::TensorOptions().dtype(at::kFloat).device(at::kCPU); + at::Tensor base = at::zeros({2, 3}, options); + at::Tensor values = at::full({1, 2, 3}, 6.0f, options); + + base.index_put_({at::indexing::None}, values); + + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "IndexPutTensorIndexNoneValue "; + write_index_result_to_file(&file, base); + file << "\n"; + file.saveFile(); +} + +TEST(TensorBodyTest, IndexPutTensorIndexTensorNoneAndSlice) { + auto options = at::TensorOptions().dtype(at::kFloat).device(at::kCPU); + at::Tensor base = at::zeros({3, 4}, options); + at::Tensor idx = tensor_from_vector_i64({2, 0}); + at::Tensor values = at::full({2, 1, 4}, 8.0f, options); + + base.index_put_({idx, at::indexing::None, at::indexing::Slice()}, values); + + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "IndexPutTensorIndexTensorNoneAndSlice "; + file << std::to_string(base.dim()) << " "; + file << std::to_string(base.numel()) << " "; + file << std::to_string(base.sizes()[0]) << " "; + file << std::to_string(base.sizes()[1]) << " "; + at::Tensor cont = base.contiguous(); + float* data = cont.data_ptr(); + file << std::to_string(data[0]) << " "; + file << std::to_string(data[4]) << " "; + file << std::to_string(data[8]) << " "; + file << std::to_string(cont.sum().item()) << " "; + file << "\n"; + file.saveFile(); +} + +TEST(TensorBodyTest, IndexPutTensorIndexNoneScalar) { + auto options = at::TensorOptions().dtype(at::kFloat).device(at::kCPU); + at::Tensor base = at::zeros({2, 3}, options); + + base.index_put_({at::indexing::None}, at::Scalar(4.0)); + + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "IndexPutTensorIndexNoneScalar "; + write_index_result_to_file(&file, base); + file << "\n"; + file.saveFile(); +} + } // namespace test } // namespace at diff --git a/test/ATen/ops/TensorPtrTest.cpp b/test/ATen/ops/TensorPtrTest.cpp index 8f46f8b..813bb4a 100644 --- a/test/ATen/ops/TensorPtrTest.cpp +++ b/test/ATen/ops/TensorPtrTest.cpp @@ -12,9 +12,18 @@ TEST(TensorBodyTest, PtrTest) { const float* const_ptr = t.const_data_ptr(); EXPECT_NE(const_ptr, nullptr); + const float* const_type_ptr = t.const_data_ptr(); + EXPECT_NE(const_type_ptr, nullptr); + const void* void_const_ptr = t.const_data_ptr(); EXPECT_NE(void_const_ptr, nullptr); + float* data_ptr = t.data_ptr(); + EXPECT_NE(data_ptr, nullptr); + + void* void_data_ptr = t.data_ptr(); + EXPECT_NE(void_data_ptr, nullptr); + float* mut_ptr = t.mutable_data_ptr(); EXPECT_NE(mut_ptr, nullptr); @@ -27,6 +36,16 @@ TEST(TensorBodyTest, PtrTest) { file.createFile(); file << "PtrTest "; file << "const_ptr[0]: " + std::to_string(const_ptr[0]) + "\n"; + file << "const_type_ptr[0]: " + std::to_string(const_type_ptr[0]) + "\n"; + file << "void_const_ptr_equal: " + << std::to_string(void_const_ptr == static_cast(const_ptr)) + << "\n"; + file << "data_ptr_equal: " + << std::to_string(static_cast(data_ptr) == void_data_ptr) << "\n"; + file << "const_type_ptr_equal: " + << std::to_string(static_cast(const_type_ptr) == + static_cast(const_ptr)) + << "\n"; mut_ptr[0] = 5.0f; file << "mut_ptr[0]: " + std::to_string(mut_ptr[0]) + "\n"; diff --git a/test/ATen/ops/TransposeTest.cpp b/test/ATen/ops/TransposeTest.cpp index 8731aa8..2d24da7 100644 --- a/test/ATen/ops/TransposeTest.cpp +++ b/test/ATen/ops/TransposeTest.cpp @@ -284,5 +284,55 @@ TEST_F(TransposeTest, TransposeSpecialValues) { file.saveFile(); } +TEST_F(TransposeTest, LargePositiveDimThrows) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "LargePositiveDimThrows "; + + try { + (void)at::transpose(tensor, 1LL << 32, 1); + file << "ok "; + } catch (const std::exception&) { + file << "exception "; + } + + at::Tensor in_place = at::zeros({2, 3, 4}, at::kFloat); + try { + (void)in_place.transpose_(1LL << 32, 1); + file << "inplace_ok "; + } catch (const std::exception&) { + file << "inplace_exception "; + } + for (int64_t i = 0; i < in_place.dim(); ++i) { + file << std::to_string(in_place.sizes()[i]) << " "; + } + file << "\n"; + file.saveFile(); +} + +TEST_F(TransposeTest, LargeNegativeDimThrows) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "LargeNegativeDimThrows "; + + try { + (void)tensor.transpose(0, -(1LL << 32)); + file << "member_ok "; + } catch (const std::exception&) { + file << "member_exception "; + } + + try { + (void)at::transpose(tensor, 0, -(1LL << 32)); + file << "free_ok "; + } catch (const std::exception&) { + file << "free_exception "; + } + file << "\n"; + file.saveFile(); +} + } // namespace test } // namespace at diff --git a/test/ATen/ops/VarTest.cpp b/test/ATen/ops/VarTest.cpp index feac64c..82b80b0 100644 --- a/test/ATen/ops/VarTest.cpp +++ b/test/ATen/ops/VarTest.cpp @@ -19,19 +19,8 @@ class VarTest : public ::testing::Test { void SetUp() override {} }; -// 返回当前用例的结果文件名 -std::string GetTestCaseResultFileName() { - std::string base = g_custom_param.get(); - std::string test_name = - ::testing::UnitTest::GetInstance()->current_test_info()->name(); - if (base.size() >= 4 && base.substr(base.size() - 4) == ".txt") { - base.resize(base.size() - 4); - } - return base + "_" + test_name + ".txt"; -} - TEST_F(VarTest, VarDim) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.createFile(); file << "VarDim "; at::Tensor input = at::ones({2, 3}, at::kFloat); @@ -44,7 +33,7 @@ TEST_F(VarTest, VarDim) { } TEST_F(VarTest, VarAll) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "VarAll "; at::Tensor input = at::ones({2, 3}, at::kFloat); @@ -57,7 +46,7 @@ TEST_F(VarTest, VarAll) { } TEST_F(VarTest, VarDims) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "VarDims "; at::Tensor input = at::ones({2, 3}, at::kFloat); @@ -71,7 +60,7 @@ TEST_F(VarTest, VarDims) { } TEST_F(VarTest, VarCorrection) { - FileManerger file(GetTestCaseResultFileName()); + FileManerger file(g_custom_param.get()); file.openAppend(); file << "VarCorrection "; at::Tensor input = at::ones({2, 3}, at::kFloat); diff --git a/test/result_cmp.sh b/test/result_cmp.sh index e5fa3c0..4311325 100755 --- a/test/result_cmp.sh +++ b/test/result_cmp.sh @@ -8,6 +8,22 @@ PADDLE_PATH=${BUILD_PATH}/paddle/ TORCH_PATH=${BUILD_PATH}/torch/ RESULT_FILE_PATH="/tmp/paddle_cpp_api_test/" +cleanup_coverage_data() { + local build_path="$1" + local gcda_count + + if [[ ! -d "$build_path" ]]; then + return + fi + + gcda_count=$(find "$build_path" -type f -name "*.gcda" | wc -l) + gcda_count=${gcda_count//[[:space:]]/} + if [[ "$gcda_count" -gt 0 ]]; then + echo "Cleaning ${gcda_count} stale .gcda coverage files under ${build_path}..." + find "$build_path" -type f -name "*.gcda" -delete + fi +} + # 保存原始终端输出,并在退出时稳定打印日志路径 LOG_FILE="${RESULT_FILE_PATH}result_cmp_$(date +%Y%m%d_%H%M%S).log" mkdir -p "${RESULT_FILE_PATH}" @@ -16,6 +32,8 @@ trap 'status=$?; printf "\nDone. Full output saved to: %s\n" "$LOG_FILE" | tee - exec > >(tee -a "$LOG_FILE") 2>&1 echo "Log file: $LOG_FILE" +cleanup_coverage_data "$BUILD_PATH" + collect_and_run_executables() { local exec_path="$1" local prefix="$2" @@ -59,8 +77,12 @@ for key in "${!TORCH_EXECUTABLES[@]}"; do done while IFS= read -r key; do - paddle_exec="${PADDLE_EXECUTABLES[$key]}" - torch_exec="${TORCH_EXECUTABLES[$key]}" + if [[ -z "$key" ]]; then + continue + fi + + paddle_exec="${PADDLE_EXECUTABLES[$key]:-}" + torch_exec="${TORCH_EXECUTABLES[$key]:-}" if [[ -z "$paddle_exec" || -z "$torch_exec" ]]; then has_mismatch=1