// Copyright (c) 2022 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/kernels/cross_kernel.h" #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/backends/gpu/gpu_launch_config.h" #include "paddle/phi/common/amp_type_traits.h" #include "paddle/phi/core/dense_tensor.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/kernels/funcs/index_calculator.h" namespace phi { template __global__ void Cross(const T* x, const T* y, T* out, const int64_t stride, const int64_t N, funcs::IndexCalculator index_calculator) { CUDA_KERNEL_LOOP_TYPE(i, N, int64_t) { int64_t offset = index_calculator(i); int64_t pos0 = offset + 0 * stride; int64_t pos1 = offset + 1 * stride; int64_t pos2 = offset + 2 * stride; using MT = typename MPTypeTrait::Type; MT x_pos0_mp = static_cast(x[pos0]); MT x_pos1_mp = static_cast(x[pos1]); MT x_pos2_mp = static_cast(x[pos2]); MT y_pos0_mp = static_cast(y[pos0]); MT y_pos1_mp = static_cast(y[pos1]); MT y_pos2_mp = static_cast(y[pos2]); out[pos0] = static_cast(x_pos1_mp * y_pos2_mp - x_pos2_mp * y_pos1_mp); out[pos1] = static_cast(x_pos2_mp * y_pos0_mp - x_pos0_mp * y_pos2_mp); out[pos2] = static_cast(x_pos0_mp * y_pos1_mp - x_pos1_mp * y_pos0_mp); } } template void CrossKernel(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& y, int axis, DenseTensor* out) { auto& input_x = x; auto& input_y = y; auto* output = out; int dim = axis; auto input_x_dims = input_x.dims(); if (dim != DDim::kMaxRank) { PADDLE_ENFORCE_EQ( dim < input_x_dims.size() && dim >= (0 - input_x_dims.size()), true, common::errors::OutOfRange( "Attr(dim) is out of range, It's expected " "to be in range of [-%d, %d]. But received Attr(dim) = %d.", input_x_dims.size(), input_x_dims.size() - 1, dim)); if (dim < 0) { dim += input_x_dims.size(); } PADDLE_ENFORCE_EQ( input_x_dims[dim] == 3, true, common::errors::InvalidArgument( "Input(X/Y).dims[dim] must be equal to 3. But received: " "Input(X/Y).dims[dim] = [%d].", input_x_dims[dim])); } else { for (auto i = 0; i < input_x_dims.size(); i++) { if (input_x_dims[i] == 3) { dim = i; break; } } PADDLE_ENFORCE_EQ(dim == DDim::kMaxRank, false, common::errors::InvalidArgument( "There must be at least one dimension 'd' so that " "Input(X/Y).dims()[d] is equal to 3. " "But received: Input(X/Y).dims() == [%s].", input_x_dims)); } if (input_x.numel() == 0 || input_y.numel() == 0) { output->Resize(input_x.dims()); dev_ctx.template Alloc(output); return; } std::vector cal_dims; std::vector left_strides; std::vector full_strides; std::vector merged_dims; for (int i = 0; i < dim; i++) { if (i == 0) { merged_dims.push_back(input_x_dims[i]); } else { merged_dims[0] *= input_x_dims[i]; } } int merge_axis = merged_dims.size(); merged_dims.push_back(input_x_dims[dim]); for (int i = dim + 1; i < input_x_dims.size(); i++) { if (i == dim + 1) { merged_dims.push_back(input_x_dims[i]); } else { merged_dims[merge_axis + 1] *= input_x_dims[i]; } } int64_t full_dim = 1; for (int i = 0; i < merged_dims.size(); i++) { full_strides.insert(full_strides.begin(), full_dim); full_dim *= merged_dims[merged_dims.size() - i - 1]; if (i == merge_axis) { continue; } cal_dims.push_back(i); } int64_t left_dim = 1; for (int i = merged_dims.size() - 1; i >= 0; i--) { if (i == merge_axis) { continue; } left_strides.insert(left_strides.begin(), left_dim); left_dim *= merged_dims[i]; } const auto* input_x_data = input_x.data(); const auto* input_y_data = input_y.data(); auto* out_data = dev_ctx.template Alloc(out); int64_t numel = x.numel(); backends::gpu::GpuLaunchConfig config = backends::gpu::GetGpuLaunchConfig1D(dev_ctx, numel / 3); auto index_calculator = funcs::IndexCalculator( merged_dims.size() - 1, cal_dims, left_strides, full_strides); Cross<<>>(input_x_data, input_y_data, out_data, full_strides[merge_axis], static_cast(numel / 3), index_calculator); } } // namespace phi PD_REGISTER_KERNEL(cross, GPU, ALL_LAYOUT, phi::CrossKernel, phi::float16, phi::bfloat16, float, double, int, int64_t, phi::complex64, phi::complex128) {}