diff --git a/onnxruntime/core/providers/cpu/nn/pool_functors.h b/onnxruntime/core/providers/cpu/nn/pool_functors.h index 476a9a0338969..7a82559fb0df5 100644 --- a/onnxruntime/core/providers/cpu/nn/pool_functors.h +++ b/onnxruntime/core/providers/cpu/nn/pool_functors.h @@ -462,7 +462,7 @@ struct AveragePool2DTask final { for (int64_t ph = 0; ph < pooled_height; ++ph) { int64_t hstart = ph * stride_h - pads[0]; int64_t hend = hstart + kernel_shape[0] * dilation_h; - hend = std::min(hend, height + pads[1]); + hend = std::min(hend, height + pads[2]); for (int64_t pw = 0; pw < pooled_width; ++pw) { int64_t wstart = pw * stride_w - pads[1]; int64_t wend = wstart + kernel_shape[1] * dilation_w; @@ -535,11 +535,11 @@ struct AveragePool3DTask { for (int64_t ph = 0; ph < pooled_height; ++ph) { int64_t hstart = ph * stride_h - pads[0]; int64_t hend = hstart + kernel_shape[0] * dilation_h; - hend = std::min(hend, height + pads[1]); + hend = std::min(hend, height + pads[3]); for (int64_t pw = 0; pw < pooled_width; ++pw) { int64_t wstart = pw * stride_w - pads[1]; int64_t wend = wstart + kernel_shape[1] * dilation_w; - wend = std::min(wend, width + pads[3]); + wend = std::min(wend, width + pads[4]); for (int64_t pd = 0; pd < pooled_depth; ++pd) { int64_t dstart = pd * stride_d - pads[2]; int64_t dend = dstart + kernel_shape[2] * dilation_d; diff --git a/onnxruntime/test/providers/cpu/nn/pool_op_test.cc b/onnxruntime/test/providers/cpu/nn/pool_op_test.cc index 8d276b7300e37..a36868c212b56 100644 --- a/onnxruntime/test/providers/cpu/nn/pool_op_test.cc +++ b/onnxruntime/test/providers/cpu/nn/pool_op_test.cc @@ -966,6 +966,78 @@ TEST(PoolTest, AveragePool_IncludePadPixel) { test.Run(OpTester::ExpectResult::kExpectSuccess, "", {kTensorrtExecutionProvider}); } +// Regression test for https://github.com/microsoft/onnxruntime/issues/26708 +// AveragePool with count_include_pad=1 and asymmetric pads (only bottom/right) +// was using incorrect pad index for hend, producing wrong results. +TEST(PoolTest, AveragePool_CountIncludePad_AsymmetricPads) { + OpTester test("AveragePool", 19); + + test.AddAttribute("auto_pad", ""); + test.AddAttribute("strides", std::vector{1, 1}); + test.AddAttribute("pads", vector{0, 0, 1, 1}); // no top/left, 1 bottom, 1 right + test.AddAttribute("kernel_shape", vector{2, 2}); + test.AddAttribute("count_include_pad", (int64_t)1); + + // Input: 2x2 all ones + std::vector x_vals = {1.0f, 1.0f, + 1.0f, 1.0f}; + std::vector x_dims = {1, 1, 2, 2}; + + // Output: 2x2 + // Top-left: (1+1+1+1)/4 = 1.0 + // Top-right: (1+0+1+0)/4 = 0.5 + // Bottom-left: (1+1+0+0)/4 = 0.5 + // Bottom-right: (1+0+0+0)/4 = 0.25 + std::vector expected_dims = {1, 1, 2, 2}; + std::vector expected_vals = {1.0f, 0.5f, + 0.5f, 0.25f}; + + test.AddInput("X", x_dims, x_vals); + test.AddOutput("Y", expected_dims, expected_vals); + // This test targets the CPU fix only. Exclude EPs whose external libraries + // (cuDNN, CoreML, etc.) also produce wrong results for this case. + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kCudaNHWCExecutionProvider, + kTensorrtExecutionProvider, kAclExecutionProvider, kOpenVINOExecutionProvider, + kDnnlExecutionProvider, kCoreMLExecutionProvider, kQnnExecutionProvider, + kDmlExecutionProvider}); +} + +// AveragePool3D with count_include_pad=1 and asymmetric pads (only back/bottom) +// Regression test for 3D path of the pad-index bug +TEST(PoolTest, AveragePool3D_CountIncludePad_AsymmetricPads) { + OpTester test("AveragePool", 19); + test.AddAttribute("auto_pad", ""); + test.AddAttribute("strides", std::vector{1, 1, 1}); + test.AddAttribute("pads", std::vector{0, 0, 0, 1, 1, 0}); // no front/top/left, 1 back, 1 bottom + test.AddAttribute("kernel_shape", std::vector{2, 2, 2}); + test.AddAttribute("count_include_pad", (int64_t)1); + // Input: 2x2x2 all ones (N=1, C=1, D=2, H=2, W=2) + std::vector x3d_vals = { + 1.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, 1.0f}; + std::vector x3d_dims = {1, 1, 2, 2, 2}; + // Output: 2x2x1 (D x H x W) + // D=0,H=0,W=0: (8 ones)/8 = 1.0 + // D=1,H=0,W=0: (4 ones + 4 padded zeros)/8 = 0.5 + // D=0,H=1,W=0: (4 ones + 4 padded zeros)/8 = 0.5 + // D=1,H=1,W=0: (2 ones + 6 padded zeros)/8 = 0.25 + std::vector expected3d_dims = {1, 1, 2, 2, 1}; + std::vector expected3d_vals = {1.0f, 0.5f, + 0.5f, 0.25f}; + test.AddInput("X", x3d_dims, x3d_vals); + test.AddOutput("Y", expected3d_dims, expected3d_vals); + // This test targets the CPU fix only. Exclude EPs whose external libraries + // (cuDNN, CoreML, etc.) also produce wrong results for this case. + test.Run(OpTester::ExpectResult::kExpectSuccess, "", + {kCudaExecutionProvider, kCudaNHWCExecutionProvider, + kTensorrtExecutionProvider, kAclExecutionProvider, kOpenVINOExecutionProvider, + kDnnlExecutionProvider, kCoreMLExecutionProvider, kQnnExecutionProvider, + kDmlExecutionProvider}); +} + // test 'strides' attribute not specified TEST(PoolTest, AveragePool_DefaultStrides) { OpTester test("AveragePool");