// 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/kernels/lerp_kernel.h" #include "paddle/phi/backends/gpu/gpu_context.h" #include "paddle/phi/common/amp_type_traits.h" #include "paddle/phi/common/data_type.h" #include "paddle/phi/core/kernel_registry.h" #include "paddle/phi/kernels/expand_kernel.h" #include "paddle/phi/kernels/funcs/broadcast_function.h" namespace phi { template struct LerpElementWiseDirectCUDAFunctor { HOSTDEVICE inline T operator()(const T x, const T y, const T weight) const { if (abs(static_cast(weight)) < 0.5f) { return x + weight * (y - x); } else { return y - (y - x) * (static_cast(1) - weight); } } }; template struct LerpScalarDirectCUDAFunctor { const WeightT* weight_; HOSTDEVICE inline LerpScalarDirectCUDAFunctor(const WeightT* weight) : weight_(weight) {} HOSTDEVICE inline T operator()(const T x, const T y) const { T weight_scalar = static_cast(weight_[0]); if (abs(static_cast(weight_[0])) < 0.5f) { return x + weight_scalar * (y - x); } else { return y - (y - x) * (static_cast(1) - weight_scalar); } } }; template void LerpKernel(const Context& dev_ctx, const DenseTensor& x, const DenseTensor& y, const DenseTensor& weight, DenseTensor* out) { if (out && out->numel() == 0) { dev_ctx.template Alloc(out); return; } int rank = out->dims().size(); PADDLE_ENFORCE_GE( rank, 0, common::errors::InvalidArgument( "The number of dimensions for LerpOp must be " "greater than or equal to 0, but the value received is %d.", rank)); dev_ctx.template Alloc(out); std::vector outputs = {out}; std::vector inputs; if (weight.numel() == 1) { inputs.reserve(2); inputs.emplace_back(&x); inputs.emplace_back(&y); if (weight.dtype() == DataType::FLOAT64) { const double* weight_ptr = weight.data(); auto functor = LerpScalarDirectCUDAFunctor(weight_ptr); funcs::BroadcastKernel(dev_ctx, inputs, &outputs, functor); } else { const T* weight_ptr = weight.data(); auto functor = LerpScalarDirectCUDAFunctor(weight_ptr); funcs::BroadcastKernel(dev_ctx, inputs, &outputs, functor); } } else { inputs.reserve(3); auto functor = LerpElementWiseDirectCUDAFunctor(); DenseTensor b_min = EmptyLike(dev_ctx, *out); if (x.dims().size() != y.dims().size() && weight.dims().size() != y.dims().size()) { if (x.dims().size() < y.dims().size() && x.dims().size() < weight.dims().size()) { // x broadcast to b_min ExpandKernel(dev_ctx, x, vectorize(b_min.dims()), &b_min); inputs.emplace_back(&b_min); inputs.emplace_back(&y); inputs.emplace_back(&weight); } else if (y.dims().size() < weight.dims().size()) { // y broadcast to b_min ExpandKernel(dev_ctx, y, vectorize(b_min.dims()), &b_min); inputs.emplace_back(&x); inputs.emplace_back(&b_min); inputs.emplace_back(&weight); } else { // weight broadcast to b_min ExpandKernel( dev_ctx, weight, vectorize(b_min.dims()), &b_min); inputs.emplace_back(&x); inputs.emplace_back(&y); inputs.emplace_back(&b_min); } } else { inputs.emplace_back(&x); inputs.emplace_back(&y); inputs.emplace_back(&weight); } funcs::BroadcastKernel(dev_ctx, inputs, &outputs, functor); } } } // namespace phi PD_REGISTER_KERNEL(lerp, GPU, ALL_LAYOUT, phi::LerpKernel, phi::float16, phi::bfloat16, float, double) {}