diff --git a/docs/OperatorKernels.md b/docs/OperatorKernels.md
index ba610515ac288..b3a4cb0c8b4b3 100644
--- a/docs/OperatorKernels.md
+++ b/docs/OperatorKernels.md
@@ -25,6 +25,7 @@ Do not modify directly.*
|||13|**T** = tensor(double), tensor(float), tensor(int32), tensor(int64)|
|||[7, 12]|**T** = tensor(double), tensor(float), tensor(int32), tensor(int64)|
|Affine|*in* X:**T**
*out* Y:**T**|1+|**T** = tensor(float)|
+|AffineGrid|*in* theta:**T1**
*in* size:**T2**
*out* grid:**T1**|20+|**T1** = tensor(double), tensor(float)
**T2** = tensor(int64)|
|And|*in* A:**T**
*in* B:**T**
*out* C:**T1**|7+|**T** = tensor(bool)
**T1** = tensor(bool)|
|ArgMax|*in* data:**T**
*out* reduced:**tensor(int64)**|13+|**T** = tensor(double), tensor(float), tensor(int32), tensor(int8), tensor(uint8)|
|||[11, 12]|**T** = tensor(double), tensor(float), tensor(int32), tensor(int8), tensor(uint8)|
diff --git a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc
index a54d999a100b8..2ca3b1cdf817e 100644
--- a/onnxruntime/core/providers/cpu/cpu_execution_provider.cc
+++ b/onnxruntime/core/providers/cpu/cpu_execution_provider.cc
@@ -960,6 +960,8 @@ class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 19, Sh
// Opset 20
class ONNX_OPERATOR_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 20, ConstantOfShape);
+class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 20, float, AffineGrid);
+class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 20, double, AffineGrid);
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 20, float, IsNaN);
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 20, double, IsNaN);
class ONNX_OPERATOR_TYPED_KERNEL_CLASS_NAME(kCpuExecutionProvider, kOnnxDomain, 20, MLFloat16, IsNaN);
@@ -2399,6 +2401,8 @@ Status RegisterOnnxOperatorKernels(KernelRegistry& kernel_registry) {
// Opset 20
BuildKernelCreateInfo,
+ BuildKernelCreateInfo,
+ BuildKernelCreateInfo,
BuildKernelCreateInfo,
BuildKernelCreateInfo,
BuildKernelCreateInfo,
diff --git a/onnxruntime/core/providers/cpu/tensor/affine_grid.cc b/onnxruntime/core/providers/cpu/tensor/affine_grid.cc
new file mode 100644
index 0000000000000..15900ba553983
--- /dev/null
+++ b/onnxruntime/core/providers/cpu/tensor/affine_grid.cc
@@ -0,0 +1,151 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#include "core/providers/cpu/tensor/affine_grid.h"
+
+#include "core/common/common.h"
+#include "core/providers/op_kernel_type_control.h"
+#include "core/util/math_cpuonly.h"
+#include
+#include "Eigen/src/Core/Map.h"
+#include
+#include "core/common/eigen_common_wrapper.h"
+
+namespace onnxruntime {
+
+#define REGISTER_KERNEL_TYPED(T) \
+ ONNX_CPU_OPERATOR_TYPED_KERNEL( \
+ AffineGrid, \
+ 20, \
+ T, \
+ KernelDefBuilder() \
+ .TypeConstraint("T1", DataTypeImpl::GetTensorType()) \
+ .TypeConstraint("T2", DataTypeImpl::GetTensorType()), \
+ AffineGrid);
+
+REGISTER_KERNEL_TYPED(float)
+REGISTER_KERNEL_TYPED(double)
+
+template
+void generate_base_grid_2d(int64_t H, int64_t W, bool align_corners, Eigen::Matrix& base_grid) {
+ Eigen::VectorXf row_vec = Eigen::VectorXf::LinSpaced(static_cast(W), -1, 1);
+ if (!align_corners) {
+ row_vec = row_vec * (W - 1) / W;
+ }
+ Eigen::VectorXf col_vec = Eigen::VectorXf::LinSpaced(static_cast(H), -1, 1);
+ if (!align_corners) {
+ col_vec = col_vec * (H - 1) / H;
+ }
+
+ base_grid.resize(static_cast(H * W), 2);
+ for (Eigen::Index j = 0; j < H; j++) {
+ for (Eigen::Index i = 0; i < W; i++) {
+ base_grid.row(j * static_cast(W) + i) << row_vec(i), col_vec(j);
+ }
+ }
+}
+
+template
+void generate_base_grid_3d(int64_t D, int64_t H, int64_t W, bool align_corners, Eigen::Matrix& base_grid) {
+ Eigen::VectorXf row_vec = Eigen::VectorXf::LinSpaced(static_cast(W), -1, 1);
+ if (!align_corners) {
+ row_vec = row_vec * (W - 1) / W;
+ }
+ Eigen::VectorXf col_vec = Eigen::VectorXf::LinSpaced(static_cast(H), -1, 1);
+ if (!align_corners) {
+ col_vec = col_vec * (H - 1) / H;
+ }
+ Eigen::VectorXf slice_vec = Eigen::VectorXf::LinSpaced(static_cast(D), -1, 1);
+ if (!align_corners) {
+ slice_vec = slice_vec * (D - 1) / D;
+ }
+
+ base_grid.resize(static_cast(D * H * W), 3);
+ for (Eigen::Index k = 0; k < D; k++) {
+ for (Eigen::Index j = 0; j < H; j++) {
+ for (Eigen::Index i = 0; i < W; i++) {
+ base_grid.row(k * static_cast(H * W) + j * static_cast(W) + i) << row_vec(i), col_vec(j), slice_vec(k);
+ }
+ }
+ }
+}
+
+template
+void affine_grid_generator_2d(const Tensor* theta, const Eigen::Matrix& base_grid_transposed, int64_t batch_num, int64_t H, int64_t W, Tensor* grid) {
+ const Eigen::StorageOptions option = Eigen::RowMajor;
+ auto theta_batch_offset = batch_num * 2 * 3;
+ const T* theta_data = theta->Data() + theta_batch_offset;
+ const Eigen::Matrix theta_R{{theta_data[0], theta_data[1]}, {theta_data[3], theta_data[4]}};
+ const Eigen::Array theta_T(theta_data[2], theta_data[5]);
+
+ auto grid_batch_offset = batch_num * H * W * 2;
+ T* grid_data = grid->MutableData() + grid_batch_offset;
+ Eigen::Map> grid_matrix(grid_data, narrow(H * W), 2);
+ grid_matrix = ((theta_R * base_grid_transposed).array().colwise() + theta_T).matrix().transpose();
+}
+
+template
+void affine_grid_generator_3d(const Tensor* theta, const Eigen::Matrix& base_grid_transposed, int64_t batch_num, int64_t D, int64_t H, int64_t W, Tensor* grid) {
+ const Eigen::StorageOptions option = Eigen::RowMajor;
+ auto theta_batch_offset = batch_num * 3 * 4;
+ const T* theta_data = theta->Data() + theta_batch_offset;
+ const Eigen::Matrix theta_R{
+ {theta_data[0], theta_data[1], theta_data[2]},
+ {theta_data[4], theta_data[5], theta_data[6]},
+ {theta_data[8], theta_data[9], theta_data[10]}};
+ const Eigen::Array theta_T(theta_data[3], theta_data[7], theta_data[11]);
+
+ auto grid_batch_offset = batch_num * D * H * W * 3;
+ T* grid_data = grid->MutableData() + grid_batch_offset;
+ Eigen::Map> grid_matrix(grid_data, narrow(D * H * W), 3);
+ grid_matrix = ((theta_R * base_grid_transposed).array().colwise() + theta_T).matrix().transpose();
+}
+
+template
+Status AffineGrid::Compute(OpKernelContext* context) const {
+ const Tensor* theta = context->Input(0);
+ const TensorShape& theta_shape = theta->Shape();
+ if (theta_shape.NumDimensions() != 3) {
+ return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "AffineGrid : Input theta tensor dimension is not 3");
+ }
+
+ const Tensor* size = context->Input(1);
+ const TensorShape& size_shape = size->Shape();
+ const int64_t* size_data = size->Data();
+
+ if (size_shape.GetDims()[0] == 4 /*&& get_check_2d_grid_sample_consistency(theta_shape, size_shape, N, C, H, W)*/) {
+ int64_t N = size_data[0], H = size_data[2], W = size_data[3];
+
+ TensorShape grid_shape{N, H, W, 2};
+ auto grid = context->Output(0, grid_shape);
+
+ Eigen::Matrix base_grid;
+ generate_base_grid_2d(H, W, align_corners_, base_grid);
+ Eigen::Matrix base_grid_transposed = base_grid.transpose();
+
+ std::function fn = [theta, base_grid_transposed, H, W, grid](ptrdiff_t batch_num) {
+ affine_grid_generator_2d(theta, base_grid_transposed, batch_num, H, W, grid);
+ };
+
+ concurrency::ThreadPool::TryBatchParallelFor(context->GetOperatorThreadPool(), narrow(N), std::move(fn), 0);
+ } else if (size_shape.GetDims()[0] == 5 /*&& get_check_2d_grid_sample_consistency(theta_shape, size_shape, N, C, H, W)*/) {
+ int64_t N = size_data[0], D = size_data[2], H = size_data[3], W = size_data[4];
+
+ TensorShape grid_shape{N, D, H, W, 3};
+ auto grid = context->Output(0, grid_shape);
+
+ Eigen::Matrix base_grid;
+ generate_base_grid_3d(D, H, W, align_corners_, base_grid);
+ Eigen::Matrix base_grid_transposed = base_grid.transpose();
+
+ std::function fn = [theta, base_grid_transposed, D, H, W, grid](ptrdiff_t batch_num) {
+ affine_grid_generator_3d(theta, base_grid_transposed, batch_num, D, H, W, grid);
+ };
+
+ concurrency::ThreadPool::TryBatchParallelFor(context->GetOperatorThreadPool(), narrow(N), std::move(fn), 0);
+ } else {
+ return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, "AffineGrid : Invalidate size - length of size should be 4 or 5.");
+ }
+ return Status::OK();
+}
+} // namespace onnxruntime
diff --git a/onnxruntime/core/providers/cpu/tensor/affine_grid.h b/onnxruntime/core/providers/cpu/tensor/affine_grid.h
new file mode 100644
index 0000000000000..5ffe660e986f2
--- /dev/null
+++ b/onnxruntime/core/providers/cpu/tensor/affine_grid.h
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#pragma once
+
+#include "core/common/common.h"
+#include "core/framework/op_kernel.h"
+
+namespace onnxruntime {
+
+template
+class AffineGrid final : public OpKernel {
+ public:
+ AffineGrid(const OpKernelInfo& info) : OpKernel(info) {
+ int64_t align_corners = info.GetAttrOrDefault("align_corners", 0);
+ align_corners_ = (align_corners != 0);
+ }
+
+ Status Compute(OpKernelContext* context) const override;
+
+ private:
+ bool align_corners_;
+};
+
+} // namespace onnxruntime
diff --git a/onnxruntime/test/providers/cpu/tensor/affine_grid_test.cc b/onnxruntime/test/providers/cpu/tensor/affine_grid_test.cc
new file mode 100644
index 0000000000000..e37e784f28930
--- /dev/null
+++ b/onnxruntime/test/providers/cpu/tensor/affine_grid_test.cc
@@ -0,0 +1,165 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+#include "core/util/math.h"
+#include "gtest/gtest.h"
+#include "test/providers/provider_test_utils.h"
+
+namespace onnxruntime {
+namespace test {
+TEST(AffineGridTest, 2d) {
+ OpTester test("AffineGrid", 20);
+ test.AddInput("theta", {1, 2, 3}, {1.0f, 0.0, 0.0f, 0.0f, 1.0, 0.0f});
+ test.AddInput("size", {4}, {1, 1, 2, 3});
+ test.AddOutput("grid", {1, 2, 3, 2},
+ {-0.6667f, -0.5000f, 0.0000f, -0.5000f, 0.6667f, -0.5000f, -0.6667f, 0.5000f, 0.0000f, 0.5000f, 0.6667f, 0.5000f});
+ test.Run();
+}
+
+// following tests code is generated with:
+// python onnxruntime/test/providers/cpu/tensor/affine_grid_test_gen.py
+TEST(AffineGridTest, test_2d_0) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)0);
+ test.AddInput("theta", {1, 2, 3}, {1.477212f, -0.173648f, 0.300000f, 0.173648f, 0.492404f, -0.500000f});
+ test.AddInput("size", {4}, {1, 1, 3, 2});
+ test.AddOutput("grid", {1, 3, 2, 2}, {-0.3228f, -0.9151f, 1.1544f, -0.7414f, -0.4386f, -0.5868f, 1.0386f, -0.4132f, -0.5544f, -0.2586f, 0.9228f, -0.0849f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_2d_1) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)0);
+ test.AddInput("theta", {2, 2, 3}, {1.477212f, -0.173648f, 0.300000f, 0.173648f, 0.492404f, -0.500000f, 1.477212f, -0.173648f, 0.300000f, 0.173648f, 0.492404f, -0.500000f});
+ test.AddInput("size", {4}, {2, 10, 2, 3});
+ test.AddOutput("grid", {2, 2, 3, 2}, {-0.5980f, -0.8620f, 0.3868f, -0.7462f, 1.3716f, -0.6304f, -0.7716f, -0.3696f, 0.2132f, -0.2538f, 1.1980f, -0.1380f, -0.5980f, -0.8620f, 0.3868f, -0.7462f, 1.3716f, -0.6304f, -0.7716f, -0.3696f, 0.2132f, -0.2538f, 1.1980f, -0.1380f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_2d_2) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)0);
+ test.AddInput("theta", {1, 2, 3}, {1.500000f, -0.866025f, -0.500000f, 0.866025f, 2.750000f, -0.500000f});
+ test.AddInput("size", {4}, {1, 1, 3, 2});
+ test.AddOutput("grid", {1, 3, 2, 2}, {-0.6726f, -2.7663f, 0.8274f, -1.9003f, -1.2500f, -0.9330f, 0.2500f, -0.0670f, -1.8274f, 0.9003f, -0.3274f, 1.7663f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_2d_3) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)0);
+ test.AddInput("theta", {2, 2, 3}, {1.500000f, -0.866025f, -0.500000f, 0.866025f, 2.750000f, -0.500000f, 1.500000f, -0.866025f, -0.500000f, 0.866025f, 2.750000f, -0.500000f});
+ test.AddInput("size", {4}, {2, 10, 2, 3});
+ test.AddOutput("grid", {2, 2, 3, 2}, {-1.0670f, -2.4524f, -0.0670f, -1.8750f, 0.9330f, -1.2976f, -1.9330f, 0.2976f, -0.9330f, 0.8750f, 0.0670f, 1.4524f, -1.0670f, -2.4524f, -0.0670f, -1.8750f, 0.9330f, -1.2976f, -1.9330f, 0.2976f, -0.9330f, 0.8750f, 0.0670f, 1.4524f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_2d_4) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)1);
+ test.AddInput("theta", {1, 2, 3}, {1.477212f, -0.173648f, 0.300000f, 0.173648f, 0.492404f, -0.500000f});
+ test.AddInput("size", {4}, {1, 1, 3, 2});
+ test.AddOutput("grid", {1, 3, 2, 2}, {-1.0036f, -1.1661f, 1.9509f, -0.8188f, -1.1772f, -0.6736f, 1.7772f, -0.3264f, -1.3509f, -0.1812f, 1.6036f, 0.1661f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_2d_5) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)1);
+ test.AddInput("theta", {2, 2, 3}, {1.477212f, -0.173648f, 0.300000f, 0.173648f, 0.492404f, -0.500000f, 1.477212f, -0.173648f, 0.300000f, 0.173648f, 0.492404f, -0.500000f});
+ test.AddInput("size", {4}, {2, 10, 2, 3});
+ test.AddOutput("grid", {2, 2, 3, 2}, {-1.0036f, -1.1661f, 0.4736f, -0.9924f, 1.9509f, -0.8188f, -1.3509f, -0.1812f, 0.1264f, -0.0076f, 1.6036f, 0.1661f, -1.0036f, -1.1661f, 0.4736f, -0.9924f, 1.9509f, -0.8188f, -1.3509f, -0.1812f, 0.1264f, -0.0076f, 1.6036f, 0.1661f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_2d_6) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)1);
+ test.AddInput("theta", {1, 2, 3}, {1.500000f, -0.866025f, -0.500000f, 0.866025f, 2.750000f, -0.500000f});
+ test.AddInput("size", {4}, {1, 1, 3, 2});
+ test.AddOutput("grid", {1, 3, 2, 2}, {-1.1340f, -4.1160f, 1.8660f, -2.3840f, -2.0000f, -1.3660f, 1.0000f, 0.3660f, -2.8660f, 1.3840f, 0.1340f, 3.1160f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_2d_7) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)1);
+ test.AddInput("theta", {2, 2, 3}, {1.500000f, -0.866025f, -0.500000f, 0.866025f, 2.750000f, -0.500000f, 1.500000f, -0.866025f, -0.500000f, 0.866025f, 2.750000f, -0.500000f});
+ test.AddInput("size", {4}, {2, 10, 2, 3});
+ test.AddOutput("grid", {2, 2, 3, 2}, {-1.1340f, -4.1160f, 0.3660f, -3.2500f, 1.8660f, -2.3840f, -2.8660f, 1.3840f, -1.3660f, 2.2500f, 0.1340f, 3.1160f, -1.1340f, -4.1160f, 0.3660f, -3.2500f, 1.8660f, -2.3840f, -2.8660f, 1.3840f, -1.3660f, 2.2500f, 0.1340f, 3.1160f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_3d_0) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)0);
+ test.AddInput("theta", {1, 3, 4}, {1.409539f, 0.000000f, 0.513030f, 0.300000f, 0.118782f, 1.969615f, -0.326352f, -0.500000f, -0.168412f, 0.086824f, 0.462708f, 1.800000f});
+ test.AddInput("size", {5}, {1, 1, 3, 2, 2});
+ test.AddOutput("grid", {1, 3, 2, 2, 3}, {-0.7468f, -1.3266f, 1.5323f, 0.6627f, -1.2078f, 1.3639f, -0.7468f, 0.6430f, 1.6191f, 0.6627f, 0.7618f, 1.4507f, -0.4048f, -1.5442f, 1.8408f, 1.0048f, -1.4254f, 1.6724f, -0.4048f, 0.4254f, 1.9276f, 1.0048f, 0.5442f, 1.7592f, -0.0627f, -1.7618f, 2.1493f, 1.3468f, -1.6430f, 1.9809f, -0.0627f, 0.2078f, 2.2361f, 1.3468f, 0.3266f, 2.0677f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_3d_1) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)0);
+ test.AddInput("theta", {2, 3, 4}, {1.409539f, 0.000000f, 0.513030f, 0.300000f, 0.118782f, 1.969615f, -0.326352f, -0.500000f, -0.168412f, 0.086824f, 0.462708f, 1.800000f, 1.409539f, 0.000000f, 0.513030f, 0.300000f, 0.118782f, 1.969615f, -0.326352f, -0.500000f, -0.168412f, 0.086824f, 0.462708f, 1.800000f});
+ test.AddInput("size", {5}, {2, 10, 2, 2, 3});
+ test.AddOutput("grid", {2, 2, 2, 3, 3}, {-0.8962f, -1.4008f, 1.6375f, 0.0435f, -1.3216f, 1.5252f, 0.9832f, -1.2424f, 1.4130f, -0.8962f, 0.5688f, 1.7243f, 0.0435f, 0.6480f, 1.6121f, 0.9832f, 0.7272f, 1.4998f, -0.3832f, -1.7272f, 2.1002f, 0.5565f, -1.6480f, 1.9879f, 1.4962f, -1.5688f, 1.8757f, -0.3832f, 0.2424f, 2.1870f, 0.5565f, 0.3216f, 2.0748f, 1.4962f, 0.4008f, 1.9625f, -0.8962f, -1.4008f, 1.6375f, 0.0435f, -1.3216f, 1.5252f, 0.9832f, -1.2424f, 1.4130f, -0.8962f, 0.5688f, 1.7243f, 0.0435f, 0.6480f, 1.6121f, 0.9832f, 0.7272f, 1.4998f, -0.3832f, -1.7272f, 2.1002f, 0.5565f, -1.6480f, 1.9879f, 1.4962f, -1.5688f, 1.8757f, -0.3832f, 0.2424f, 2.1870f, 0.5565f, 0.3216f, 2.0748f, 1.4962f, 0.4008f, 1.9625f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_3d_2) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)0);
+ test.AddInput("theta", {1, 3, 4}, {0.259808f, 0.000000f, -0.150000f, -0.500000f, -1.299038f, 1.500000f, -2.250000f, -0.500000f, 1.375000f, 4.763140f, 2.381570f, 0.300000f});
+ test.AddInput("size", {5}, {1, 1, 3, 2, 2});
+ test.AddOutput("grid", {1, 3, 2, 2, 3}, {-0.5299f, 0.8995f, -4.3568f, -0.2701f, -0.3995f, -2.9818f, -0.5299f, 2.3995f, 0.4064f, -0.2701f, 1.1005f, 1.7814f, -0.6299f, -0.6005f, -2.7691f, -0.3701f, -1.8995f, -1.3941f, -0.6299f, 0.8995f, 1.9941f, -0.3701f, -0.3995f, 3.3691f, -0.7299f, -2.1005f, -1.1814f, -0.4701f, -3.3995f, 0.1936f, -0.7299f, -0.6005f, 3.5818f, -0.4701f, -1.8995f, 4.9568f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_3d_3) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)0);
+ test.AddInput("theta", {2, 3, 4}, {0.259808f, 0.000000f, -0.150000f, -0.500000f, -1.299038f, 1.500000f, -2.250000f, -0.500000f, 1.375000f, 4.763140f, 2.381570f, 0.300000f, 0.259808f, 0.000000f, -0.150000f, -0.500000f, -1.299038f, 1.500000f, -2.250000f, -0.500000f, 1.375000f, 4.763140f, 2.381570f, 0.300000f});
+ test.AddInput("size", {5}, {2, 10, 2, 2, 3});
+ test.AddOutput("grid", {2, 2, 2, 3, 3}, {-0.5982f, 0.7410f, -4.1890f, -0.4250f, -0.1250f, -3.2724f, -0.2518f, -0.9910f, -2.3557f, -0.5982f, 2.2410f, 0.5741f, -0.4250f, 1.3750f, 1.4908f, -0.2518f, 0.5090f, 2.4075f, -0.7482f, -1.5090f, -1.8075f, -0.5750f, -2.3750f, -0.8908f, -0.4018f, -3.2410f, 0.0259f, -0.7482f, -0.0090f, 2.9557f, -0.5750f, -0.8750f, 3.8724f, -0.4018f, -1.7410f, 4.7890f, -0.5982f, 0.7410f, -4.1890f, -0.4250f, -0.1250f, -3.2724f, -0.2518f, -0.9910f, -2.3557f, -0.5982f, 2.2410f, 0.5741f, -0.4250f, 1.3750f, 1.4908f, -0.2518f, 0.5090f, 2.4075f, -0.7482f, -1.5090f, -1.8075f, -0.5750f, -2.3750f, -0.8908f, -0.4018f, -3.2410f, 0.0259f, -0.7482f, -0.0090f, 2.9557f, -0.5750f, -0.8750f, 3.8724f, -0.4018f, -1.7410f, 4.7890f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_3d_4) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)1);
+ test.AddInput("theta", {1, 3, 4}, {1.409539f, 0.000000f, 0.513030f, 0.300000f, 0.118782f, 1.969615f, -0.326352f, -0.500000f, -0.168412f, 0.086824f, 0.462708f, 1.800000f});
+ test.AddInput("size", {5}, {1, 1, 3, 2, 2});
+ test.AddOutput("grid", {1, 3, 2, 2, 3}, {-1.6226f, -2.2620f, 1.4189f, 1.1965f, -2.0245f, 1.0821f, -1.6226f, 1.6772f, 1.5925f, 1.1965f, 1.9147f, 1.2557f, -1.1095f, -2.5884f, 1.8816f, 1.7095f, -2.3508f, 1.5448f, -1.1095f, 1.3508f, 2.0552f, 1.7095f, 1.5884f, 1.7184f, -0.5965f, -2.9147f, 2.3443f, 2.2226f, -2.6772f, 2.0075f, -0.5965f, 1.0245f, 2.5179f, 2.2226f, 1.2620f, 2.1811f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_3d_5) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)1);
+ test.AddInput("theta", {2, 3, 4}, {1.409539f, 0.000000f, 0.513030f, 0.300000f, 0.118782f, 1.969615f, -0.326352f, -0.500000f, -0.168412f, 0.086824f, 0.462708f, 1.800000f, 1.409539f, 0.000000f, 0.513030f, 0.300000f, 0.118782f, 1.969615f, -0.326352f, -0.500000f, -0.168412f, 0.086824f, 0.462708f, 1.800000f});
+ test.AddInput("size", {5}, {2, 10, 2, 2, 3});
+ test.AddOutput("grid", {2, 2, 2, 3, 3}, {-1.6226f, -2.2620f, 1.4189f, -0.2130f, -2.1433f, 1.2505f, 1.1965f, -2.0245f, 1.0821f, -1.6226f, 1.6772f, 1.5925f, -0.2130f, 1.7960f, 1.4241f, 1.1965f, 1.9147f, 1.2557f, -0.5965f, -2.9147f, 2.3443f, 0.8130f, -2.7960f, 2.1759f, 2.2226f, -2.6772f, 2.0075f, -0.5965f, 1.0245f, 2.5179f, 0.8130f, 1.1433f, 2.3495f, 2.2226f, 1.2620f, 2.1811f, -1.6226f, -2.2620f, 1.4189f, -0.2130f, -2.1433f, 1.2505f, 1.1965f, -2.0245f, 1.0821f, -1.6226f, 1.6772f, 1.5925f, -0.2130f, 1.7960f, 1.4241f, 1.1965f, 1.9147f, 1.2557f, -0.5965f, -2.9147f, 2.3443f, 0.8130f, -2.7960f, 2.1759f, 2.2226f, -2.6772f, 2.0075f, -0.5965f, 1.0245f, 2.5179f, 0.8130f, 1.1433f, 2.3495f, 2.2226f, 1.2620f, 2.1811f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_3d_6) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)1);
+ test.AddInput("theta", {1, 3, 4}, {0.259808f, 0.000000f, -0.150000f, -0.500000f, -1.299038f, 1.500000f, -2.250000f, -0.500000f, 1.375000f, 4.763140f, 2.381570f, 0.300000f});
+ test.AddInput("size", {5}, {1, 1, 3, 2, 2});
+ test.AddOutput("grid", {1, 3, 2, 2, 3}, {-0.6098f, 1.5490f, -8.2197f, -0.0902f, -1.0490f, -5.4697f, -0.6098f, 4.5490f, 1.3066f, -0.0902f, 1.9510f, 4.0566f, -0.7598f, -0.7010f, -5.8381f, -0.2402f, -3.2990f, -3.0881f, -0.7598f, 2.2990f, 3.6881f, -0.2402f, -0.2990f, 6.4381f, -0.9098f, -2.9510f, -3.4566f, -0.3902f, -5.5490f, -0.7066f, -0.9098f, 0.0490f, 6.0697f, -0.3902f, -2.5490f, 8.8197f});
+ test.Run();
+}
+
+TEST(AffineGridTest, test_3d_7) {
+ OpTester test("AffineGrid", 20);
+ test.AddAttribute("align_corners", (int64_t)1);
+ test.AddInput("theta", {2, 3, 4}, {0.259808f, 0.000000f, -0.150000f, -0.500000f, -1.299038f, 1.500000f, -2.250000f, -0.500000f, 1.375000f, 4.763140f, 2.381570f, 0.300000f, 0.259808f, 0.000000f, -0.150000f, -0.500000f, -1.299038f, 1.500000f, -2.250000f, -0.500000f, 1.375000f, 4.763140f, 2.381570f, 0.300000f});
+ test.AddInput("size", {5}, {2, 10, 2, 2, 3});
+ test.AddOutput("grid", {2, 2, 2, 3, 3}, {-0.6098f, 1.5490f, -8.2197f, -0.3500f, 0.2500f, -6.8447f, -0.0902f, -1.0490f, -5.4697f, -0.6098f, 4.5490f, 1.3066f, -0.3500f, 3.2500f, 2.6816f, -0.0902f, 1.9510f, 4.0566f, -0.9098f, -2.9510f, -3.4566f, -0.6500f, -4.2500f, -2.0816f, -0.3902f, -5.5490f, -0.7066f, -0.9098f, 0.0490f, 6.0697f, -0.6500f, -1.2500f, 7.4447f, -0.3902f, -2.5490f, 8.8197f, -0.6098f, 1.5490f, -8.2197f, -0.3500f, 0.2500f, -6.8447f, -0.0902f, -1.0490f, -5.4697f, -0.6098f, 4.5490f, 1.3066f, -0.3500f, 3.2500f, 2.6816f, -0.0902f, 1.9510f, 4.0566f, -0.9098f, -2.9510f, -3.4566f, -0.6500f, -4.2500f, -2.0816f, -0.3902f, -5.5490f, -0.7066f, -0.9098f, 0.0490f, 6.0697f, -0.6500f, -1.2500f, 7.4447f, -0.3902f, -2.5490f, 8.8197f});
+ test.Run();
+}
+} // namespace test
+} // namespace onnxruntime
diff --git a/onnxruntime/test/providers/cpu/tensor/affine_grid_test_gen.py b/onnxruntime/test/providers/cpu/tensor/affine_grid_test_gen.py
new file mode 100644
index 0000000000000..22bad6f1be534
--- /dev/null
+++ b/onnxruntime/test/providers/cpu/tensor/affine_grid_test_gen.py
@@ -0,0 +1,111 @@
+import argparse
+
+import numpy as np
+import torch
+from torch.nn.functional import affine_grid
+
+opset_version = 20
+parser = argparse.ArgumentParser(description="Generate test cases for the AffineGrid operator.")
+parser.add_argument("--dim", type=int, choices=[2, 3], help="Dimension of the test cases (2 or 3)")
+args = parser.parse_args()
+
+if args.dim is None or args.dim == 2:
+ align_corners_options = [False, True]
+ angles = [10, 60]
+ translations = [np.array([0.3, -0.5]), np.array([-0.5, -0.5])]
+ scales = [np.array([1.5, 0.5]), np.array([3.0, 5.5])]
+ sizes = [[1, 1, 3, 2], [2, 10, 2, 3]]
+ test_count = 0
+
+ for align_corners in align_corners_options:
+ for angle, translation, scale in zip(angles, translations, scales):
+ for size in sizes:
+ theta = np.array([], dtype=np.float32)
+ for _ in range(size[0]):
+ angle_radian = (angle / 180.0) * np.pi
+ theta = np.append(
+ theta,
+ [
+ np.cos(angle_radian) * scale[0],
+ -np.sin(angle_radian),
+ translation[0],
+ np.sin(angle_radian),
+ np.cos(angle_radian) * scale[1],
+ translation[1],
+ ],
+ )
+ theta = theta.reshape(size[0], 2, 3)
+ theta = torch.Tensor(theta)
+ grid = affine_grid(theta, size, align_corners=align_corners)
+
+ # Print the C++ code for the test case
+ print(f"TEST(AffineGridTest, test_2d_{test_count}) {{")
+ print(f' OpTester test("AffineGrid", {opset_version});')
+ print(f' test.AddAttribute("align_corners", (int64_t){1 if align_corners else 0});')
+ print(
+ f" test.AddInput(\"theta\", {{{theta.shape[0]}, {theta.shape[1]}, {theta.shape[2]}}}, {{{', '.join([f'{x:.6f}f' for x in theta.flatten()])}}});"
+ )
+ print(
+ f' test.AddInput("size", {{{len(size)}}}, {{{size[0]}, {size[1]}, {size[2]}, {size[3]}}});'
+ )
+ print(
+ f" test.AddOutput(\"grid\", {{{size[0]}, {size[2]}, {size[3]}, 2}}, {{{', '.join([f'{x:.4f}f' for x in grid.flatten()])}}});"
+ )
+ print(" test.Run();")
+ print("}\n")
+ test_count += 1
+
+
+if args.dim is None or args.dim == 3:
+ align_corners_options = [False, True]
+ angles = [[10, 20], [60, -30]]
+ translations = [np.array([0.3, -0.5, 1.8]), np.array([-0.5, -0.5, 0.3])]
+ scales = [np.array([1.5, 2.0, 0.5]), np.array([0.3, 3.0, 5.5])]
+ sizes = [[1, 1, 3, 2, 2], [2, 10, 2, 2, 3]]
+ test_count = 0
+
+ for align_corners in align_corners_options:
+ for angle, translation, scale in zip(angles, translations, scales):
+ for size in sizes:
+ theta = np.array([], dtype=np.float32)
+ for _ in range(size[0]):
+ angle_radian_x = (angle[0] / 180.0) * np.pi
+ angle_radian_y = (angle[1] / 180.0) * np.pi
+ rot_matrix_x = np.array(
+ [
+ [1, 0, 0],
+ [0, np.cos(angle_radian_x), -np.sin(angle_radian_x)],
+ [0, np.sin(angle_radian_x), np.cos(angle_radian_x)],
+ ]
+ )
+ rot_matrix_y = np.array(
+ [
+ [np.cos(angle_radian_y), 0, np.sin(angle_radian_y)],
+ [0, 1, 0],
+ [-np.sin(angle_radian_y), 0, np.cos(angle_radian_y)],
+ ]
+ )
+ rot_matrix = np.matmul(rot_matrix_x, rot_matrix_y)
+ rot_matrix = rot_matrix * scale.reshape(3, 1)
+ rot_matrix = np.append(rot_matrix, np.reshape(translation, (3, 1)), axis=1)
+ theta = np.append(theta, rot_matrix.flatten())
+ theta = theta.reshape(size[0], 3, 4)
+ theta = torch.Tensor(theta)
+ grid = affine_grid(theta, size, align_corners=align_corners)
+
+ # Print the C++ code for the test case
+ print(f"TEST(AffineGridTest, test_3d_{test_count}) {{")
+ print(f' OpTester test("AffineGrid", {opset_version});')
+ print(f' test.AddAttribute("align_corners", (int64_t){1 if align_corners else 0});')
+ print(
+ f" test.AddInput(\"theta\", {{{theta.shape[0]}, {theta.shape[1]}, {theta.shape[2]}}}, {{{', '.join([f'{x:.6f}f' for x in theta.flatten()])}}});"
+ )
+ print(
+ f' test.AddInput("size", {{{len(size)}}}, {{{size[0]}, {size[1]}, {size[2]}, {size[3]}, {size[4]}}});'
+ )
+ print(
+ f" test.AddOutput(\"grid\", {{{size[0]}, {size[2]}, {size[3]}, {size[4]}, 3}}, {{{', '.join([f'{x:.4f}f' for x in grid.flatten()])}}});"
+ )
+ print(" test.Run();")
+ print("}\n")
+ test_count += 1