// 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. #pragma once #include #include #include #include "glog/logging.h" #include "paddle/phi/core/enforce.h" #include "paddle/phi/core/tensor_utils.h" #include "paddle/phi/kernels/full_kernel.h" #include "paddle/phi/kernels/impl/determinant_kernel_impl.h" #include "paddle/phi/kernels/slogdeterminant_kernel.h" namespace phi { // T is not complex template T _sign(T val) { return static_cast(T(0) < val) - (val < T(0)); } // T is complex template T _sign(T det, T modulus) { return det / modulus; } template struct SlogDeterminantFunctor { void operator()(const Context& dev_ctx, const DenseTensor& input, int64_t rank, int64_t batch_count, DenseTensor* output) { std::vector input_vec; std::vector sign_vec; std::vector log_vec; std::vector output_vec; TensorToVector(input, dev_ctx, &input_vec); for (int64_t i = 0; i < batch_count; ++i) { // maybe can be parallel auto begin_iter = input_vec.begin() + i * rank * rank; auto end_iter = input_vec.begin() + (i + 1) * rank * rank; std::vector sub_vec(begin_iter, end_iter); // get every square matrix data typename detail::EigenMatrix::MatrixType matrix(rank, rank); for (int64_t i = 0; i < rank; ++i) { for (int64_t j = 0; j < rank; ++j) { matrix(i, j) = sub_vec[rank * i + j]; } } VLOG(2) << "det value: " << matrix.determinant(); VLOG(2) << "matrix val: " << matrix; auto det_val = matrix.determinant(); sign_vec.push_back(_sign(det_val)); det_val >= 0 ? log_vec.push_back(std::log(det_val)) : log_vec.push_back(std::log(std::abs( det_val))); // for computing log value of a negative value. } // merge sign_vec and log_vec as final output_vec output_vec.insert(output_vec.end(), sign_vec.begin(), sign_vec.end()); output_vec.insert(output_vec.end(), log_vec.begin(), log_vec.end()); TensorFromVector(output_vec, dev_ctx, output); } }; template struct SlogDeterminantFunctor, Context> { void operator()(const Context& dev_ctx, const DenseTensor& input, int64_t rank, int64_t batch_count, DenseTensor* output) { using MatrixType = Eigen::Matrix, Eigen::Dynamic, Eigen::Dynamic>; std::vector> input_vec; std::vector> sign_vec; std::vector> log_vec; std::vector> output_vec; TensorToVector(input, dev_ctx, &input_vec); for (int64_t i = 0; i < batch_count; ++i) { // maybe can be parallel auto begin_iter = input_vec.begin() + i * rank * rank; auto end_iter = input_vec.begin() + (i + 1) * rank * rank; std::vector> sub_vec( begin_iter, end_iter); // get every square matrix data MatrixType matrix(rank, rank); for (int64_t i = 0; i < rank; ++i) { for (int64_t j = 0; j < rank; ++j) { matrix(i, j) = static_cast>(sub_vec[rank * i + j]); } } VLOG(2) << "det value: " << matrix.determinant(); VLOG(2) << "matrix val: " << matrix; std::complex det_val = matrix.determinant(); T abs_det_val = std::abs(det_val); sign_vec.push_back(static_cast>( _sign(det_val, static_cast>(abs_det_val)))); log_vec.push_back(static_cast>(std::log(abs_det_val))); } // merge sign_vec and log_vec as final output_vec output_vec.insert(output_vec.end(), sign_vec.begin(), sign_vec.end()); output_vec.insert(output_vec.end(), log_vec.begin(), log_vec.end()); TensorFromVector(output_vec, dev_ctx, output); } }; template void SlogDeterminantKernel(const Context& dev_ctx, const DenseTensor& x, DenseTensor* out) { auto input_dim = vectorize(x.dims()); auto input_dim_size = input_dim.size(); // shape [*, M, M], check whether it contains 0 in '*'. if (input_dim.size() > 2) { bool size_0 = false; std::vector tmp_dim_vec(input_dim.begin(), input_dim.end() - 2); for (size_t i = 0; i < tmp_dim_vec.size(); ++i) { if (tmp_dim_vec[i] == 0) { size_0 = true; break; } } if (size_0) { tmp_dim_vec.insert(tmp_dim_vec.begin(), 2); // make the output dims as same as numpy out->Resize(tmp_dim_vec); dev_ctx.template Alloc(out); return; } } auto batch_count = detail::GetBatchCount(x.dims()); VLOG(2) << "input dim:" << x.dims(); PADDLE_ENFORCE_GE( input_dim_size, 2, errors::InvalidArgument("the input matrix dimension size should greater " "than or equal to 2.")); PADDLE_ENFORCE_EQ( input_dim[input_dim_size - 1], input_dim[input_dim_size - 2], errors::InvalidArgument("the input matrix should be square matrix.")); auto rank = input_dim[input_dim_size - 1]; // square matrix length SlogDeterminantFunctor()(dev_ctx, x, rank, batch_count, out); std::vector output_dim_vec(input_dim.begin(), input_dim.end() - 2); if (input_dim.size() == static_cast(2)) { // when input is a two-dimension matrix, The det value is a number. output_dim_vec = {}; } output_dim_vec.insert(output_dim_vec.begin(), 2); // make the output dims as same as numpy auto output_dims = make_ddim(output_dim_vec); out->Resize(output_dims); VLOG(2) << "output dim:" << out->dims(); } template struct SlogDeterminantV2Functor { void operator()(const Context& dev_ctx, const DenseTensor& input, int64_t rank, int64_t batch_count, DenseTensor* sign, DenseTensor* logdet) { if (input.numel() == 0) { dev_ctx.template Alloc(sign); if (sign->numel() > 0) { Full(dev_ctx, sign->dims(), static_cast(1), sign); } dev_ctx.template Alloc(logdet); if (logdet->numel() > 0) { Full( dev_ctx, logdet->dims(), static_cast>(0), logdet); } return; } std::vector input_vec; T* sign_data = dev_ctx.template Alloc(sign); T* logdet_data = dev_ctx.template Alloc(logdet); TensorToVector(input, dev_ctx, &input_vec); for (int64_t i = 0; i < batch_count; ++i) { // maybe can be parallel auto begin_iter = input_vec.begin() + i * rank * rank; auto end_iter = input_vec.begin() + (i + 1) * rank * rank; std::vector sub_vec(begin_iter, end_iter); // get every square matrix data typename detail::EigenMatrix::MatrixType matrix(rank, rank); for (int64_t i = 0; i < rank; ++i) { for (int64_t j = 0; j < rank; ++j) { matrix(i, j) = sub_vec[rank * i + j]; } } VLOG(2) << "det value: " << matrix.determinant(); VLOG(2) << "matrix val: " << matrix; T det_val = matrix.determinant(); sign_data[i] = _sign(det_val); det_val >= 0 ? logdet_data[i] = std::log(det_val) : logdet_data[i] = std::log(std::abs( det_val)); // for computing log value of a negative value. } } }; template struct SlogDeterminantV2Functor, Context> { void operator()(const Context& dev_ctx, const DenseTensor& input, int64_t rank, int64_t batch_count, DenseTensor* sign, DenseTensor* logdet) { if (input.numel() == 0) { dev_ctx.template Alloc>(sign); dev_ctx.template Alloc>(sign); if (sign->numel() > 0) { Full, Context>( dev_ctx, sign->dims(), static_cast>(1), sign); } dev_ctx.template Alloc(logdet); if (logdet->numel() > 0) { Full( dev_ctx, logdet->dims(), static_cast>(0), logdet); } return; } using MatrixType = Eigen::Matrix, Eigen::Dynamic, Eigen::Dynamic>; using Complex_T = typename dtype::complex; std::vector input_vec; Complex_T* sign_data = dev_ctx.template Alloc(sign); T* logdet_data = dev_ctx.template Alloc(logdet); TensorToVector(input, dev_ctx, &input_vec); for (int64_t i = 0; i < batch_count; ++i) { // maybe can be parallel auto begin_iter = input_vec.begin() + i * rank * rank; auto end_iter = input_vec.begin() + (i + 1) * rank * rank; std::vector> sub_vec( begin_iter, end_iter); // get every square matrix data MatrixType matrix(rank, rank); for (int64_t i = 0; i < rank; ++i) { for (int64_t j = 0; j < rank; ++j) { matrix(i, j) = static_cast>(sub_vec[rank * i + j]); } } VLOG(2) << "det value: " << matrix.determinant(); VLOG(2) << "matrix val: " << matrix; std::complex det_val = matrix.determinant(); T abs_det_val = std::abs(det_val); T epsilon = std::numeric_limits::epsilon(); if (abs_det_val <= epsilon) { sign_data[i] = Complex_T(0.0, 0.0); logdet_data[i] = -std::numeric_limits::infinity(); } else { sign_data[i] = static_cast( _sign(det_val, static_cast>(abs_det_val))); logdet_data[i] = std::log(abs_det_val); } } } }; template void SlogDeterminantV2Kernel(const Context& dev_ctx, const DenseTensor& x, DenseTensor* sign, DenseTensor* logdet) { auto input_dim = vectorize(x.dims()); auto input_dim_size = input_dim.size(); auto batch_count = detail::GetBatchCount(x.dims()); VLOG(3) << "input dim:" << x.dims(); PADDLE_ENFORCE_GE( input_dim_size, 2, errors::InvalidArgument("the input matrix dimension size should greater " "than or equal to 2.")); PADDLE_ENFORCE_EQ( input_dim[input_dim_size - 1], input_dim[input_dim_size - 2], errors::InvalidArgument("the input matrix should be square matrix.")); auto rank = input_dim[input_dim_size - 1]; // square matrix length SlogDeterminantV2Functor()( dev_ctx, x, rank, batch_count, sign, logdet); VLOG(3) << "sign dim:" << sign->dims(); } } // namespace phi