/* Copyright (c) 2021 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. */ #include "paddle/phi/infermeta/binary.h" #include "paddle/phi/core/ddim.h" #include "paddle/phi/kernels/funcs/common_shape.h" namespace phi { void DotInferMeta(const MetaTensor& x, const MetaTensor& y, MetaTensor* out) { auto x_dims = x.dims(); auto x_rank = static_cast(x_dims.size()); PADDLE_ENFORCE_EQ(true, 1 == x_rank || 2 == x_rank, phi::errors::PreconditionNotMet( "ShapeError: The dimensions of input tensor X (%s) " "should be 1 or 2", x_dims.to_str())); auto y_dims = y.dims(); PADDLE_ENFORCE_EQ( true, x_rank == static_cast(y_dims.size()), phi::errors::PreconditionNotMet( "ShapeError: The shape of input tensor Y: %s should match with " "input tenosr X: %s", y_dims.to_str(), x_dims.to_str())); bool shape_match = true; for (size_t i = 0; i < x_rank; ++i) { if (x_dims[i] != y_dims[i]) { shape_match = false; break; } } PADDLE_ENFORCE_EQ(true, shape_match, phi::errors::PreconditionNotMet( "ShapeError: The shape of input tensor X: %s should " "be exactly the same " "with input tensor Y: %s", x_dims.to_str(), y_dims.to_str())); x_dims[x_dims.size() - 1] = 1; out->set_dims(x_dims); out->set_dtype(x.dtype()); out->set_layout(x.layout()); } void MatmulInferMeta(const MetaTensor& x, const MetaTensor& y, bool trans_x, bool trans_y, MetaTensor* out) { std::vector dims_x = phi::vectorize(x.dims()); std::vector dims_y = phi::vectorize(y.dims()); auto ndims_x = dims_x.size(); auto ndims_y = dims_y.size(); PADDLE_ENFORCE_GT(ndims_x, 0UL, phi::errors::InvalidArgument( "The Input(x) dims size must be greater than 0," " but reviced dims size is 0. ")); PADDLE_ENFORCE_GT(ndims_y, 0UL, phi::errors::InvalidArgument( "The Input(y) dims size must be greater than 0," " but reviced dims size is 0. ")); bool x_broadcasted = false, y_broadcasted = false; if (ndims_x == 1) { dims_x.insert(dims_x.begin(), 1); ndims_x = 2; x_broadcasted = true; } if (ndims_y == 1) { dims_y.push_back(1); ndims_y = 2; y_broadcasted = true; } size_t M, N; if (trans_x) { M = dims_x[ndims_x - 1]; } else { M = dims_x[ndims_x - 2]; } if (trans_y) { N = dims_y[ndims_y - 2]; } else { N = dims_y[ndims_y - 1]; } std::vector new_dims; if (ndims_x > ndims_y) { new_dims.assign(dims_x.begin(), dims_x.end() - 2); } else if (ndims_x < ndims_y) { new_dims.assign(dims_y.begin(), dims_y.end() - 2); } else { new_dims.reserve(ndims_x); for (size_t i = 0; i < ndims_x - 2; ++i) { new_dims.push_back(std::max(dims_x[i], dims_y[i])); } } if (!x_broadcasted) { new_dims.push_back(M); } if (!y_broadcasted) { new_dims.push_back(N); } if (x_broadcasted && y_broadcasted) { new_dims.push_back(1); } auto ddim_out = phi::make_ddim(new_dims); out->set_dims(ddim_out); out->set_dtype(x.dtype()); out->set_layout(x.layout()); } void ElementwiseInferMeta(const MetaTensor& x, const MetaTensor& y, MetaTensor* out) { return ElementwiseRawInferMeta(x, y, -1, std::move(out)); } void ElementwiseRawInferMeta(const MetaTensor& x, const MetaTensor& y, int axis, MetaTensor* out) { if (x.dims() != y.dims()) { auto x_dims = x.dims(); auto y_dims = y.dims(); int max_dim = std::max(x_dims.size(), y_dims.size()); if (x_dims.size() == y_dims.size()) { PADDLE_ENFORCE_EQ((axis == -1) || (axis == 0), true, phi::errors::InvalidArgument( "axis should be -1 or 0 while the dimension of " "tensor X (%s) is equal to the dimension of " "tensor Y (%s), but received axis: %s", x_dims.size(), y_dims.size(), axis)); } PADDLE_ENFORCE_EQ((axis >= (-1 * max_dim)) && (axis < max_dim), true, phi::errors::InvalidArgument( "The axis range must be [%s, %s), but axis is %s. " "Please set the axis again.", -1 * max_dim, max_dim, axis)); axis = (axis < 0 ? (std::abs(x_dims.size() - y_dims.size()) + axis + 1) : axis); std::vector x_dims_array(max_dim); std::vector y_dims_array(max_dim); std::vector out_dims_array(max_dim); funcs::GetBroadcastDimsArrays(x_dims, y_dims, x_dims_array.data(), y_dims_array.data(), out_dims_array.data(), max_dim, axis); auto out_dims = phi::make_ddim(out_dims_array); out->set_dims(out_dims); } else { out->set_dims(x.dims()); } out->set_dtype(x.dtype()); out->set_layout(x.layout()); out->share_lod(x); } void HuberLossInferMeta(const MetaTensor& input, const MetaTensor& label, float delta, MetaTensor* out, MetaTensor* residual, MetaConfig config) { auto input_dims = input.dims(); auto label_dims = label.dims(); PADDLE_ENFORCE_EQ(input_dims.size(), label_dims.size(), phi::errors::InvalidArgument( "Input(input) rank and Input(label) rank should be " "same, but received input rank(%d) != label rank(%d)", input_dims.size(), label_dims.size())); bool contain_unknown_dim = phi::contain_unknown_dim(input_dims) || phi::contain_unknown_dim(label_dims); if (config.is_runtime || !contain_unknown_dim) { PADDLE_ENFORCE_EQ( input_dims, label_dims, phi::errors::InvalidArgument( "The Input(input) and Input(label) should have the same " "shape, but received input shape [%s] != label shape [%s]", input_dims, label_dims)); } auto out_dims = label_dims; residual->set_dims(out_dims); out->set_dims(out_dims); out->share_lod(input); } void CrossInferMeta(const MetaTensor& x, const MetaTensor& y, int axis, MetaTensor* out) { auto x_dim = x.dims(); auto y_dim = y.dims(); auto dim = axis; bool dims_match = phi::funcs::CheckDims(x_dim, y_dim); PADDLE_ENFORCE_EQ( dims_match, true, phi::errors::InvalidArgument("The 'shape' of Input(X) should be equal to " "the 'shape' of Input(Y). But received " "Input(X).dimensions = [%s], " "Input(Y).dimensions = [%s]", x_dim, y_dim)); if (dim != DDim::kMaxRank) { PADDLE_ENFORCE_EQ( dim < x_dim.size() && dim >= (0 - x_dim.size()), true, phi::errors::OutOfRange( "Attr(dim) is out of range, It's expected " "to be in range of [-%d, %d]. But received Attr(dim) = %d.", x_dim.size(), x_dim.size() - 1, dim)); if (dim < 0) { dim += x_dim.size(); } PADDLE_ENFORCE_EQ(x_dim[dim] == 3 && y_dim[dim] == 3, true, phi::errors::InvalidArgument( "Input(X/Y).dims()[dim] should be equal to 3." "But received Input(X/Y).dims()[dim] = %d.", x_dim[dim])); } out->set_dims(x_dim); out->set_dtype(x.dtype()); out->set_layout(x.layout()); out->share_lod(x); } void Atan2InferMeta(const MetaTensor& x, const MetaTensor& y, MetaTensor* out) { auto in_dims = x.dims(); out->set_dims(in_dims); } void BCELossInferMeta(const MetaTensor& input, const MetaTensor& label, MetaTensor* out, MetaConfig config) { auto input_dims = input.dims(); auto label_dims = label.dims(); int rank = input_dims.size(); PADDLE_ENFORCE_EQ(rank, label_dims.size(), phi::errors::InvalidArgument( "Input(X) and Input(Label) shall have the same rank." "But received: the rank of Input(X) is [%d], " "the rank of Input(Label) is [%d].", rank, label_dims.size())); bool check = true; if ((!config.is_runtime) && (phi::product(input_dims) <= 0 || phi::product(label_dims) <= 0)) { check = false; } if (check) { PADDLE_ENFORCE_EQ(input_dims, label_dims, phi::errors::InvalidArgument( "Input(X) and Input(Label) shall have the same " "shape. But received: the shape of Input(X) is " "[%s], the shape of Input(Label) is [%s].", input_dims, label_dims)); } out->set_dims(input_dims); out->share_lod(input); } } // namespace phi