diff --git a/paddle/fluid/operators/common_infer_shape_functions.cc b/paddle/fluid/operators/common_infer_shape_functions.cc index ce622d7501f90d0b6d1e9f4f50627fe0038f013e..c10bba74ce7c7d41a48f28ca9b1c528300131924 100644 --- a/paddle/fluid/operators/common_infer_shape_functions.cc +++ b/paddle/fluid/operators/common_infer_shape_functions.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/fluid/operators/common_infer_shape_functions.h" + #include #include @@ -28,6 +29,7 @@ class InferShapeContext; namespace paddle { namespace operators { namespace details { + inline void GetBroadcastDimsArrays(const framework::DDim &x_dims, const framework::DDim &y_dims, int *x_dims_array, int *y_dims_array, @@ -76,6 +78,20 @@ inline void GetBroadcastDimsArrays(const framework::DDim &x_dims, } } } + +framework::DDim BroadcastTwoDims(const framework::DDim &x_dims, + const framework::DDim &y_dims, int axis) { + int max_dim = std::max(x_dims.size(), y_dims.size()); + axis = (axis == -1 ? std::abs(x_dims.size() - y_dims.size()) : axis); + std::vector x_dims_array(max_dim); + std::vector y_dims_array(max_dim); + std::vector out_dims_array(max_dim); + GetBroadcastDimsArrays(x_dims, y_dims, x_dims_array.data(), + y_dims_array.data(), out_dims_array.data(), max_dim, + axis); + return framework::make_ddim(out_dims_array); +} + } // namespace details // shape input(0) -> output(0) without change. @@ -153,16 +169,9 @@ void BinaryOpBroadcastInferShape(framework::InferShapeContext *ctx) { ctx->ShareDim(x_name, /*->*/ out_name); ctx->ShareLoD(x_name, /*->*/ out_name); } else { - int max_dim = std::max(x_dims.size(), y_dims.size()); int axis = ctx->Attrs().Get("axis"); - axis = (axis == -1 ? std::abs(x_dims.size() - y_dims.size()) : axis); - std::vector x_dims_array(max_dim); - std::vector y_dims_array(max_dim); - std::vector out_dims_array(max_dim); - details::GetBroadcastDimsArrays(x_dims, y_dims, x_dims_array.data(), - y_dims_array.data(), out_dims_array.data(), - max_dim, axis); - ctx->SetOutputDim(out_name, framework::make_ddim(out_dims_array)); + auto out_dims = details::BroadcastTwoDims(x_dims, y_dims, axis); + ctx->SetOutputDim(out_name, out_dims); ctx->ShareLoD(x_name, /*->*/ out_name); } } diff --git a/paddle/fluid/operators/common_infer_shape_functions.h b/paddle/fluid/operators/common_infer_shape_functions.h index 922d5262abc42dfb19f72ddaa47c85e448b84208..2c28db4324e6dce5de34ed729e68e3910b929df2 100644 --- a/paddle/fluid/operators/common_infer_shape_functions.h +++ b/paddle/fluid/operators/common_infer_shape_functions.h @@ -28,7 +28,10 @@ class InferShapeContext; namespace paddle { namespace operators { - +namespace details { +framework::DDim BroadcastTwoDims(const framework::DDim& x_dims, + const framework::DDim& y_dims, int axis = -1); +} // shape input(0) -> output(0) without change. void UnaryOpUnchangedInferShape(framework::InferShapeContext* ctx); // shape input(0) -> output(0) without change, check if axis in range [-Rank(x), diff --git a/paddle/fluid/pybind/pybind.cc b/paddle/fluid/pybind/pybind.cc index 736669fa4ef921f21d2972e322f8d8250512cd63..a7e3cd82d26a44d49930c0001f04a39819b85140 100644 --- a/paddle/fluid/pybind/pybind.cc +++ b/paddle/fluid/pybind/pybind.cc @@ -54,6 +54,7 @@ limitations under the License. */ #include "paddle/fluid/memory/allocation/allocator_strategy.h" #include "paddle/fluid/memory/allocation/mmap_allocator.h" #include "paddle/fluid/operators/activation_op.h" +#include "paddle/fluid/operators/common_infer_shape_functions.h" #include "paddle/fluid/operators/py_func_op.h" #include "paddle/fluid/platform/cpu_helper.h" #include "paddle/fluid/platform/cpu_info.h" @@ -467,6 +468,12 @@ PYBIND11_MODULE(core_noavx, m) { << ", sci_mode=" << print_opt.sci_mode; }); + m.def("broadcast_shape", [](const std::vector &x_dim, + const std::vector &y_dim) { + return vectorize(operators::details::BroadcastTwoDims( + make_ddim(x_dim), make_ddim(y_dim), -1)); + }); + m.def( "_append_python_callable_object_and_return_id", [](py::object py_obj) -> size_t { diff --git a/python/paddle/__init__.py b/python/paddle/__init__.py index c8e0d830f4e174fbf96cbd6ab3f32d5b71b2090c..50c1142c7bfb621183193b54860e288d0cef6452 100755 --- a/python/paddle/__init__.py +++ b/python/paddle/__init__.py @@ -200,6 +200,8 @@ from .tensor.math import isfinite #DEFINE_ALIAS from .tensor.math import isinf #DEFINE_ALIAS from .tensor.math import isnan #DEFINE_ALIAS from .tensor.math import prod #DEFINE_ALIAS +from .tensor.math import broadcast_shape #DEFINE_ALIAS + from .tensor.random import multinomial #DEFINE_ALIAS from .tensor.random import standard_normal from .tensor.random import normal @@ -220,7 +222,7 @@ from .tensor.search import index_select #DEFINE_ALIAS from .tensor.search import nonzero #DEFINE_ALIAS from .tensor.search import sort #DEFINE_ALIAS -from .tensor.to_string import set_printoptions +from .tensor.to_string import set_printoptions #DEFINE_ALIAS from .framework.random import seed #DEFINE_ALIAS from .framework.random import get_cuda_rng_state #DEFINE_ALIAS diff --git a/python/paddle/fluid/tests/unittests/test_broadcast_shape.py b/python/paddle/fluid/tests/unittests/test_broadcast_shape.py new file mode 100644 index 0000000000000000000000000000000000000000..b4ac096a6968548feecb6616d157ae31996e6f4f --- /dev/null +++ b/python/paddle/fluid/tests/unittests/test_broadcast_shape.py @@ -0,0 +1,35 @@ +# Copyright (c) 2020 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. + +import unittest +import numpy as np +import paddle + + +class TestBroadcastShape(unittest.TestCase): + def test_result(self): + shape = paddle.broadcast_shape([2, 1, 3], [1, 3, 1]) + self.assertEqual(shape, [2, 3, 3]) + + shape = paddle.broadcast_shape( + [-1, 1, 3], [1, 3, 1]) #support compile time infershape + self.assertEqual(shape, [-1, 3, 3]) + + def test_error(self): + self.assertRaises(ValueError, paddle.broadcast_shape, [2, 1, 3], + [3, 3, 1]) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/paddle/tensor/__init__.py b/python/paddle/tensor/__init__.py index 43e6c9654c4d8b39f7058a9838cec419523f8534..2a9820d4a90d39dca9aedf72c4cf3394067ff83d 100755 --- a/python/paddle/tensor/__init__.py +++ b/python/paddle/tensor/__init__.py @@ -164,6 +164,8 @@ from .math import isnan #DEFINE_ALIAS from .math import prod #DEFINE_ALIAS from .math import all #DEFINE_ALIAS from .math import any #DEFINE_ALIAS +from .math import broadcast_shape #DEFINE_ALIAS + from .random import multinomial #DEFINE_ALIAS from .random import standard_normal from .random import normal @@ -194,4 +196,4 @@ from .stat import median #DEFINE_ALIAS # from .tensor import Tensor #DEFINE_ALIAS # from .tensor import LoDTensor #DEFINE_ALIAS # from .tensor import LoDTensorArray #DEFINE_ALIAS -from .to_string import set_printoptions +from .to_string import set_printoptions #DEFINE_ALIAS diff --git a/python/paddle/tensor/math.py b/python/paddle/tensor/math.py index 36793e0769672250e40510b89a9813e76e73ee9e..c83e788538e1c19bf909cad3b7c7d689e691d5a6 100755 --- a/python/paddle/tensor/math.py +++ b/python/paddle/tensor/math.py @@ -121,7 +121,8 @@ __all__ = [ 'kron', 'isfinite', 'isinf', - 'isnan' + 'isnan', + 'broadcast_shape' ] # yapf: enable. @@ -2133,3 +2134,30 @@ def any(x, axis=None, keepdim=False, name=None): outputs={'Out': out}, attrs=attrs) return out + +def broadcast_shape(x_shape, y_shape): + """ + The function returns the shape of doing operation with broadcasting on tensors of x_shape and y_shape, please refer to :ref:`user_guide_broadcasting` for more details. + + Args: + x_shape (list[int]|tuple[int]): A shape of tensor. + y_shape (list[int]|tuple[int]): A shape of tensor. + + + Returns: + list[int], the result shape. + + Examples: + .. code-block:: python + + import paddle + + shape = paddle.broadcast_shape([2, 1, 3], [1, 3, 1]) + # [2, 3, 3] + + # shape = paddle.broadcast_shape([2, 1, 3], [3, 3, 1]) + # ValueError (terminated with error message). + + """ + + return core.broadcast_shape(x_shape, y_shape)