Repository: sdpython/mlprodict (language: python)

Issue:
Add runtime for operator AveragePool



Discussion / hints:

Fix patch:
diff --git a/mlprodict/onnxrt/ops_cpu/_op_list.py b/mlprodict/onnxrt/ops_cpu/_op_list.py
index 6bceac841..a7b5f1be0 100644
--- a/mlprodict/onnxrt/ops_cpu/_op_list.py
+++ b/mlprodict/onnxrt/ops_cpu/_op_list.py
@@ -7,17 +7,18 @@
 
 from ._op import OpRun
 from .op_abs import Abs
+from .op_acos import Acos
+from .op_acosh import Acosh
 from .op_add import Add
 from .op_and import And
 from .op_argmax import ArgMax
 from .op_argmin import ArgMin
 from .op_array_feature_extractor import ArrayFeatureExtractor
-from .op_acos import Acos
-from .op_acosh import Acosh
 from .op_asin import Asin
 from .op_asinh import Asinh
 from .op_atan import Atan
 from .op_atanh import Atanh
+from .op_average_pool import AveragePool
 from .op_batch_normalization import BatchNormalization, BatchNormalization_14
 from .op_binarizer import Binarizer
 from .op_cast import Cast
diff --git a/mlprodict/onnxrt/ops_cpu/op_average_pool.py b/mlprodict/onnxrt/ops_cpu/op_average_pool.py
new file mode 100644
index 000000000..204e20f6a
--- /dev/null
+++ b/mlprodict/onnxrt/ops_cpu/op_average_pool.py
@@ -0,0 +1,195 @@
+# -*- encoding: utf-8 -*-
+# pylint: disable=E0203,E1101,C0111
+"""
+@file
+@brief Runtime operator.
+"""
+import itertools
+import numpy
+from ..shape_object import ShapeObjectFct
+from ._op import OpRun
+
+
+def _get_pad_shape(auto_pad, input_spatial_shape, kernel_spatial_shape,
+                   strides_spatial, output_spatial_shape):
+    pad_shape = [0] * len(input_spatial_shape)
+    if auto_pad in ('SAME_UPPER', 'SAME_LOWER'):
+        for i in range(len(input_spatial_shape)):  # pylint: disable=C0200
+            pad_shape[i] = (
+                (output_spatial_shape[i] - 1) * strides_spatial[i] +
+                kernel_spatial_shape[i] - input_spatial_shape[i])
+    elif auto_pad == 'VALID':
+        pass
+    if len(pad_shape) == 0:
+        raise RuntimeError(  # pragma: no cover
+            "Unable to compute pad shape, auto_pad=%r, "
+            "input_spatial_shape=%r, kernel_spatial_shape=%r, "
+            "strides_spatial=%r." % (
+                auto_pad, input_spatial_shape, kernel_spatial_shape,
+                strides_spatial))
+    return pad_shape
+
+
+def _get_output_shape(auto_pad, input_spatial_shape, kernel_spatial_shape,
+                      strides_spatial):
+    out_shape = [0] * len(input_spatial_shape)
+    if auto_pad in ('SAME_UPPER', 'SAME_LOWER'):
+        for i in range(len(input_spatial_shape)):  # pylint: disable=C0200
+            out_shape[i] = int(
+                numpy.ceil(
+                    float(input_spatial_shape[i]) /
+                    float(strides_spatial[i])))
+    elif auto_pad == 'VALID':
+        for i in range(len(input_spatial_shape)):  # pylint: disable=C0200
+            out_shape[i] = int(
+                numpy.ceil(
+                    float(input_spatial_shape[i] -
+                          (kernel_spatial_shape[i] - 1)) /
+                    float(strides_spatial[i])))
+    if len(out_shape) == 0:
+        raise RuntimeError(  # pragma: no cover
+            "Unable to compute output shape, auto_pad=%r, "
+            "input_spatial_shape=%r, kernel_spatial_shape=%r, "
+            "strides_spatial=%r." % (
+                auto_pad, input_spatial_shape, kernel_spatial_shape,
+                strides_spatial))
+    if min(out_shape) <= 0:
+        raise RuntimeError(  # pragma: no cover
+            "output shape cannot be null or negative, out_shape=%r, "
+            "auto_pad=%r, input_spatial_shape=%r, "
+            "kernel_spatial_shape=%r, strides_spatial=%r." % (
+                out_shape, auto_pad, input_spatial_shape,
+                kernel_spatial_shape, strides_spatial))
+    return out_shape
+
+
+def _pool(padded, x_shape, kernel_shape, strides_shape,
+          out_shape, pad_shape, pooling_type, count_include_pad=0):
+    spatial_size = len(x_shape) - 2
+    y = numpy.zeros([x_shape[0], x_shape[1]] + list(out_shape))
+
+    for shape in itertools.product(
+            range(x_shape[0]),
+            range(x_shape[1]),
+            *[range(int(
+                (x_shape[i + 2] + pad_shape[i] - kernel_shape[i]) /
+                strides_shape[i] + 1)) for i in range(spatial_size)]):
+        window = padded[shape[0], shape[1]]
+        window_vals = numpy.array([window[i] for i in list(
+            itertools.product(
+                *[range(strides_shape[i] * shape[i + 2],
+                        strides_shape[i] * shape[i + 2] + kernel_shape[i])
+                  for i in range(spatial_size)]))])
+        if pooling_type == 'AVG':
+            f = numpy.average
+        elif pooling_type == 'MAX':
+            f = numpy.max
+        else:
+            raise NotImplementedError(  # pragma: no cover
+                'Pooling type {} does not support. Should be AVG, MAX.'
+                ''.format(pooling_type))
+
+        if count_include_pad == 1 and pooling_type == 'AVG':
+            y[shape] = f(window_vals)
+        else:
+            y[shape] = f(window_vals[numpy.where(~numpy.isnan(window_vals))])
+    return y.astype(numpy.float32)
+
+
+class AveragePool(OpRun):
+
+    atts = {'auto_pad': b'NOTSET',
+            'ceil_mode': 0,
+            'count_include_pad': 0,
+            'kernel_shape': [],
+            'pads': [],
+            'strides': []}
+
+    def __init__(self, onnx_node, desc=None, **options):
+        OpRun.__init__(self, onnx_node, desc=desc,
+                       expected_attributes=AveragePool.atts,
+                       **options)
+
+    def _run(self, x):  # pylint: disable=W0221
+        if self.ceil_mode != 0:
+            raise RuntimeError(
+                "ceil_mode != 0, runtime not implemented yet.")
+        if len(self.strides) == 0:
+            strides = [1] * (len(x.shape) - 2)
+        else:
+            strides = self.strides
+        kernel_shape = list(self.kernel_shape)
+        auto_pad = (
+            'VALID' if self.auto_pad == b'NOTSET'
+            else self.auto_pad.decode('ascii'))
+
+        if len(self.pads) == 0:
+            pad_shape = [0] * (len(x.shape) - 2)
+            x_shape = x.shape[2:]
+            padded = x
+        elif len(self.pads) == 4:
+            pad_top, pad_bottom, pad_left, pad_right = self.pads
+            pad_shape = [pad_top + pad_bottom, pad_left + pad_right]
+            x_shape = numpy.array(x.shape[2:]) + numpy.array(pad_shape)
+            const = numpy.nan if self.count_include_pad == 0 else 0
+            padded = numpy.pad(
+                x, ((0, 0), (0, 0),
+                    (pad_top, pad_bottom), (pad_left, pad_right)),
+                mode='constant', constant_values=const)
+        else:
+            pad_shape = self.pads
+            x_shape = x.shape[2:]
+            padded = x
+
+        if auto_pad in ('SAME_LOWER', 'SAME_UPPER'):
+            const = numpy.nan if self.count_include_pad == 0 else 0
+            out_shape = _get_output_shape(
+                auto_pad, x_shape, kernel_shape, strides)
+            pad_shape = _get_pad_shape(
+                auto_pad, x_shape, kernel_shape, strides, out_shape)
+            if auto_pad == 'SAME_LOWER':
+                pad_bottom = pad_shape[0] // 2
+                pad_top = pad_shape[0] - pad_bottom
+                pad_right = pad_shape[1] // 2
+                pad_left = pad_shape[1] - pad_right
+            else:
+                pad_top = pad_shape[0] // 2
+                pad_bottom = pad_shape[0] - pad_top
+                pad_left = pad_shape[1] // 2
+                pad_right = pad_shape[1] - pad_left
+            padded = numpy.pad(
+                padded, ((0, 0), (0, 0), (pad_top, pad_bottom),
+                         (pad_left, pad_right)),
+                mode='constant', constant_values=const)
+        else:
+            out_shape = _get_output_shape(
+                auto_pad, x_shape, kernel_shape, strides)
+
+        pooling_type = 'AVG'
+        res = _pool(padded, x.shape, kernel_shape, strides,
+                    out_shape, pad_shape, pooling_type,
+                    count_include_pad=self.count_include_pad)
+        return (res, )
+
+    def _infer_shapes(self, x):  # pylint: disable=W0221
+        kernel_shape = list(self.kernel_shape)
+        auto_pad = 'VALID' if self.auto_pad == 'NOTSET' else self.auto_pad
+
+        def compute_shape(xshape):
+            if len(self.strides) == 0:
+                strides = [1] * (len(xshape) - 2)
+            else:
+                strides = self.strides
+            out_shape = _get_output_shape(
+                auto_pad, xshape[2:], kernel_shape, strides)
+            return out_shape
+
+        return (ShapeObjectFct(
+            compute_shape, x, name="AveragePool", dtype=x.dtype), )
+
+    def _infer_types(self, x):  # pylint: disable=W0221
+        return (x, )
+
+    def _infer_sizes(self, *args):  # pylint: disable=W0221
+        res = self.run(*args)
+        return (dict(temp=0), ) + res


Test patch:
diff --git a/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py b/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py
index 4548cbd95..ad3163ac9 100644
--- a/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py
+++ b/_unittests/ut_onnxrt/test_onnxrt_python_runtime_.py
@@ -30,6 +30,7 @@
     OnnxArgMin_11, OnnxArgMin,
     OnnxBatchNormalization,
     OnnxAcos, OnnxAcosh, OnnxAsin, OnnxAsinh, OnnxAtan, OnnxAtanh,
+    OnnxAveragePool,
     OnnxCast, OnnxCeil, OnnxClip,
     OnnxCompress,
     OnnxConcat, OnnxConv, OnnxConvTranspose,
@@ -85,6 +86,8 @@
 from mlprodict.onnxrt.validate.validate_python import validate_python_inference
 from mlprodict.onnxrt.ops_cpu.op_batch_normalization import (
     _batchnorm_test_mode, _batchnorm_training_mode)
+from mlprodict.onnxrt.ops_cpu.op_average_pool import (
+    _get_output_shape, _pool, _get_pad_shape)
 from mlprodict.onnxrt.ops_cpu.op_global_average_pool import _global_average_pool
 from mlprodict.onnxrt.ops_cpu._op_onnx_numpy import (  # pylint: disable=E0611,E0401
     topk_element_min_double, topk_element_max_double,
@@ -374,6 +377,14 @@ def test_onnxt_runtime_abs_debug(self):
             self.common_test_onnxt_runtime_unary(
                 OnnxAbs, numpy.abs, debug=True)
 
+    @wraplog()
+    def test_onnxt_runtime_acos(self):
+        self.common_test_onnxt_runtime_unary(OnnxAcos, numpy.arccos)
+
+    @wraplog()
+    def test_onnxt_runtime_acosh(self):
+        self.common_test_onnxt_runtime_unary(OnnxAcosh, numpy.arccosh)
+
     @wraplog()
     def test_onnxt_runtime_add(self):
         self.common_test_onnxt_runtime_binary(OnnxAdd, numpy.add)
@@ -567,14 +578,6 @@ def test_onnxt_runtime_argmin_12(self):
         self.common_expected_shapes_types(
             oinf, {'X': X}, got, OnnxArgMin_12, model_def)
 
-    @wraplog()
-    def test_onnxt_runtime_acos(self):
-        self.common_test_onnxt_runtime_unary(OnnxAcos, numpy.arccos)
-
-    @wraplog()
-    def test_onnxt_runtime_acosh(self):
-        self.common_test_onnxt_runtime_unary(OnnxAcosh, numpy.arccosh)
-
     @wraplog()
     def test_onnxt_runtime_asin(self):
         self.common_test_onnxt_runtime_unary(OnnxAsin, numpy.arcsin)
@@ -613,6 +616,256 @@ def atan2(y, x):
         self.assertEqualArray(
             numpy.arctan2(y_val, x_val), atan2(y_val, x_val), decimal=5)
 
+    def _expect_average_pool(self, node, inputs, outputs, opset=None):
+        if opset is None:
+            opset = get_opset_number_from_onnx()
+        ginputs = [
+            onnx.helper.make_tensor_value_info(
+                node.input[0], TensorProto.FLOAT, []),  # pylint: disable=E1101,
+        ]
+        goutputs = [
+            onnx.helper.make_tensor_value_info(
+                node.output[0], TensorProto.FLOAT, []),  # pylint: disable=E1101,
+        ]
+        model_def = onnx.helper.make_model(
+            opset_imports=[onnx.helper.make_operatorsetid('', opset)],
+            graph=onnx.helper.make_graph(
+                name='test_average_pool', inputs=ginputs, outputs=goutputs,
+                nodes=[node]))
+        oinf = OnnxInference(model_def)
+        got = oinf.run({n: v for n, v in zip(node.input, inputs)})
+        self.assertEqual(len(got), 1)
+        self.assertEqualArray(outputs[0], got['y'])
+
+    @wraplog()
+    def test_onnxt_runtime_average_pool(self):
+        node = onnx.helper.make_node(
+            'AveragePool', inputs=['x'], outputs=['y'],
+            kernel_shape=[2, 2], auto_pad='SAME_UPPER')
+        x = numpy.random.randn(1, 3, 32, 32).astype(numpy.float32)
+        x_shape = numpy.shape(x)
+        kernel_shape = (2, 2)
+        strides = (1, 1)
+        out_shape = _get_output_shape(
+            'SAME_UPPER', x_shape[2:], kernel_shape, strides)
+        pad_shape = _get_pad_shape(
+            'SAME_UPPER', x_shape[2:], kernel_shape, strides, out_shape)
+        pad_top = pad_shape[0] // 2
+        pad_bottom = pad_shape[0] - pad_top
+        pad_left = pad_shape[1] // 2
+        pad_right = pad_shape[1] - pad_left
+        padded = numpy.pad(
+            x, ((0, 0), (0, 0), (pad_top, pad_bottom), (pad_left, pad_right)),
+            mode='constant', constant_values=numpy.nan)
+        y = _pool(
+            padded, x_shape, kernel_shape, strides, out_shape, pad_shape, 'AVG')
+        self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        node = onnx.helper.make_node(
+            'AveragePool', inputs=['x'], outputs=['y'],
+            kernel_shape=[3, 3], pads=[2, 2, 2, 2],
+            count_include_pad=1)
+        x = numpy.random.randn(1, 3, 28, 28).astype(numpy.float32)
+        x_shape = numpy.shape(x)
+        kernel_shape = (3, 3)
+        strides = (1, 1)
+        pad_bottom = 2
+        pad_top = 2
+        pad_right = 2
+        pad_left = 2
+        pad_shape = [pad_top + pad_bottom, pad_left + pad_right]
+        out_shape = _get_output_shape(
+            'VALID', numpy.add(x_shape[2:], pad_shape), kernel_shape, strides)
+        padded = numpy.pad(
+            x, ((0, 0), (0, 0), (pad_top, pad_bottom), (pad_left, pad_right)),
+            mode='constant', constant_values=0)
+        y = _pool(
+            padded, x_shape, kernel_shape, strides, out_shape,
+            pad_shape, 'AVG', count_include_pad=1)
+        self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        node = onnx.helper.make_node(
+            'AveragePool', inputs=['x'], outputs=['y'],
+            kernel_shape=[2, 2], auto_pad='SAME_LOWER')
+        x = numpy.random.randn(1, 3, 32, 32).astype(numpy.float32)
+        x_shape = numpy.shape(x)
+        kernel_shape = (2, 2)
+        strides = (1, 1)
+        out_shape = _get_output_shape(
+            'SAME_LOWER', x_shape[2:], kernel_shape, strides)
+        pad_shape = _get_pad_shape(
+            'SAME_LOWER', x_shape[2:], kernel_shape, strides, out_shape)
+        pad_bottom = pad_shape[0] // 2
+        pad_top = pad_shape[0] - pad_bottom
+        pad_right = pad_shape[1] // 2
+        pad_left = pad_shape[1] - pad_right
+        padded = numpy.pad(
+            x, ((0, 0), (0, 0), (pad_top, pad_bottom), (pad_left, pad_right)),
+            mode='constant', constant_values=numpy.nan)
+        y = _pool(
+            padded, x_shape, kernel_shape, strides, out_shape, pad_shape, 'AVG')
+        self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        node = onnx.helper.make_node(
+            'AveragePool', inputs=['x'], outputs=['y'],
+            kernel_shape=[3, 3], pads=[2, 2, 2, 2])
+        x = numpy.random.randn(1, 3, 28, 28).astype(numpy.float32)
+        x_shape = numpy.shape(x)
+        kernel_shape = (3, 3)
+        strides = (1, 1)
+        pad_bottom = 2
+        pad_top = 2
+        pad_right = 2
+        pad_left = 2
+        pad_shape = [pad_top + pad_bottom, pad_left + pad_right]
+        out_shape = _get_output_shape(
+            'VALID', numpy.add(x_shape[2:], pad_shape), kernel_shape, strides)
+        padded = numpy.pad(
+            x, ((0, 0), (0, 0), (pad_top, pad_bottom), (pad_left, pad_right)),
+            mode='constant', constant_values=numpy.nan)
+        y = _pool(
+            padded, x_shape, kernel_shape, strides, out_shape, pad_shape, 'AVG')
+        self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        node = onnx.helper.make_node(
+            'AveragePool', inputs=['x'], outputs=['y'],
+            kernel_shape=[2])
+        x = numpy.random.randn(1, 3, 32).astype(numpy.float32)
+        x_shape = numpy.shape(x)
+        kernel_shape = [2]
+        strides = [1]
+        out_shape = _get_output_shape(
+            'VALID', x_shape[2:], kernel_shape, strides)
+        padded = x
+        y = _pool(padded, x_shape, kernel_shape,
+                  strides, out_shape, [0], 'AVG')
+        self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        node = onnx.helper.make_node(
+            'AveragePool', inputs=['x'], outputs=['y'],
+            kernel_shape=[2, 2])
+        x = numpy.random.randn(1, 3, 32, 32).astype(numpy.float32)
+        x_shape = numpy.shape(x)
+        kernel_shape = (2, 2)
+        strides = (1, 1)
+        out_shape = _get_output_shape(
+            'VALID', x_shape[2:], kernel_shape, strides)
+        padded = x
+        y = _pool(
+            padded, x_shape, kernel_shape, strides, out_shape, (0, 0), 'AVG')
+        self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        node = onnx.helper.make_node(
+            'AveragePool', inputs=['x'], outputs=['y'],
+            kernel_shape=[5, 5], strides=[3, 3])
+        x = numpy.random.randn(1, 3, 32, 32).astype(numpy.float32)
+        x_shape = numpy.shape(x)
+        kernel_shape = (5, 5)
+        strides = (3, 3)
+        out_shape = _get_output_shape(
+            'VALID', x_shape[2:], kernel_shape, strides)
+        padded = x
+        y = _pool(
+            padded, x_shape, kernel_shape, strides, out_shape, (0, 0), 'AVG')
+        self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        node = onnx.helper.make_node(
+            'AveragePool', inputs=['x'], outputs=['y'],
+            kernel_shape=[2, 2, 2])
+        x = numpy.random.randn(1, 3, 32, 32, 32).astype(numpy.float32)
+        x_shape = numpy.shape(x)
+        kernel_shape = [2, 2, 2]
+        strides = [1, 1, 1]
+        out_shape = _get_output_shape(
+            'VALID', x_shape[2:], kernel_shape, strides)
+        padded = x
+        y = _pool(
+            padded, x_shape, kernel_shape, strides, out_shape, [0, 0, 0], 'AVG')
+        self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        python_tested.append(OnnxAveragePool)
+
+    @wraplog()
+    @unittest.skipIf(True, "not implemented yet")
+    def test_onnxt_runtime_average_pool_ceil(self):
+        node = onnx.helper.make_node(
+            'AveragePool', inputs=['x'], outputs=['y'],
+            kernel_shape=[3, 3], strides=[2, 2], ceil_mode=True)
+        x = numpy.array([[[
+            [1, 2, 3, 4],
+            [5, 6, 7, 8],
+            [9, 10, 11, 12],
+            [13, 14, 15, 16]]]]).astype(numpy.float32)
+        y = numpy.array([[[
+            [6, 7.5], [12, 13.5]]]]).astype(numpy.float32)
+        self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+    @wraplog()
+    def test_onnxt_runtime_average_pool_big(self):
+
+        with self.subTest(name='test_averagepool_2d_precomputed_pads'):
+            node = onnx.helper.make_node(
+                'AveragePool', inputs=['x'], outputs=['y'],
+                kernel_shape=[5, 5], pads=[2, 2, 2, 2])
+            x = numpy.array([[[
+                [1, 2, 3, 4, 5],
+                [6, 7, 8, 9, 10],
+                [11, 12, 13, 14, 15],
+                [16, 17, 18, 19, 20],
+                [21, 22, 23, 24, 25]]]]).astype(numpy.float32)
+            y = numpy.array([[[[7, 7.5, 8, 8.5, 9],
+                            [9.5, 10, 10.5, 11, 11.5],
+                            [12, 12.5, 13, 13.5, 14],
+                            [14.5, 15, 15.5, 16, 16.5],
+                            [17, 17.5, 18, 18.5, 19]]]]).astype(numpy.float32)
+            self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        with self.subTest(name='test_averagepool_2d_precomputed_pads_count_include_pad'):
+            node = onnx.helper.make_node(
+                'AveragePool', inputs=['x'], outputs=['y'],
+                kernel_shape=[5, 5], pads=[2, 2, 2, 2], count_include_pad=1)
+            x = numpy.array([[[
+                [1, 2, 3, 4, 5],
+                [6, 7, 8, 9, 10],
+                [11, 12, 13, 14, 15],
+                [16, 17, 18, 19, 20],
+                [21, 22, 23, 24, 25]]]]).astype(numpy.float32)
+            y = numpy.array([[[[2.5200, 3.6000, 4.8000, 4.0800, 3.2400],
+                            [4.5600, 6.4000, 8.4000, 7.0400, 5.5200],
+                            [7.2000, 10.0000, 13.0000, 10.8000, 8.4000],
+                            [6.9600, 9.6000, 12.4000, 10.2400, 7.9200],
+                            [6.1200, 8.4000, 10.8000, 8.8800, 6.8400]]]]).astype(numpy.float32)
+            self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        with self.subTest(name='test_averagepool_2d_precomputed_same_upper'):
+            node = onnx.helper.make_node(
+                'AveragePool', inputs=['x'], outputs=['y'],
+                kernel_shape=[3, 3], strides=[2, 2], auto_pad='SAME_UPPER')
+            x = numpy.array([[[
+                [1, 2, 3, 4, 5],
+                [6, 7, 8, 9, 10],
+                [11, 12, 13, 14, 15],
+                [16, 17, 18, 19, 20],
+                [21, 22, 23, 24, 25]]]]).astype(numpy.float32)
+            y = numpy.array([[[[4, 5.5, 7],
+                            [11.5, 13, 14.5],
+                            [19, 20.5, 22]]]]).astype(numpy.float32)
+            self._expect_average_pool(node, inputs=[x], outputs=[y])
+
+        with self.subTest(name='test_averagepool_2d_precomputed_strides'):
+            node = onnx.helper.make_node(
+                'AveragePool', inputs=['x'], outputs=['y'],
+                kernel_shape=[2, 2], strides=[2, 2])
+            x = numpy.array([[[
+                [1, 2, 3, 4, 5],
+                [6, 7, 8, 9, 10],
+                [11, 12, 13, 14, 15],
+                [16, 17, 18, 19, 20],
+                [21, 22, 23, 24, 25]]]]).astype(numpy.float32)
+            y = numpy.array([[[[4, 6],
+                            [14, 16]]]]).astype(numpy.float32)
+            self._expect_average_pool(node, inputs=[x], outputs=[y])
+
     @wraplog()
     def test_onnxt_runtime_batch_normalization(self):
         # input size: (1, 2, 1, 3)
@@ -3971,5 +4224,5 @@ def test_op_constant(self):
 
 if __name__ == "__main__":
     # Working
-    # TestOnnxrtPythonRuntime().test_make_constant()
+    # TestOnnxrtPythonRuntime().test_onnxt_runtime_average_pool()
     unittest.main()