未验证 提交 9bc54c84 编写于 作者: B BiynXu 提交者: GitHub

[PHI]Move slogdeterminant op to phi (#44547)

* Move slogdeterminant op to phi

* Add yaml and unit test for slogdeterminant
上级 fb80048d
...@@ -12,9 +12,10 @@ ...@@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "paddle/fluid/operators/determinant_op.h"
#include "paddle/fluid/framework/infershape_utils.h" #include "paddle/fluid/framework/infershape_utils.h"
#include "paddle/fluid/framework/op_proto_maker.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/phi/core/infermeta_utils.h" #include "paddle/phi/core/infermeta_utils.h"
#include "paddle/phi/infermeta/backward.h" #include "paddle/phi/infermeta/backward.h"
#include "paddle/phi/infermeta/unary.h" #include "paddle/phi/infermeta/unary.h"
...@@ -170,19 +171,19 @@ REGISTER_OPERATOR(determinant_grad, ...@@ -170,19 +171,19 @@ REGISTER_OPERATOR(determinant_grad,
ops::DeterminantGradOp, ops::DeterminantGradOp,
DeterminantGradInferShapeFunctor); DeterminantGradInferShapeFunctor);
DECLARE_INFER_SHAPE_FUNCTOR(slogdeterminant,
SlogDeterminantInferShapeFunctor,
PD_INFER_META(phi::UnchangedInferMeta));
REGISTER_OPERATOR(slogdeterminant, REGISTER_OPERATOR(slogdeterminant,
ops::SlogDeterminantOp, ops::SlogDeterminantOp,
ops::SlogDeterminantOpMaker, ops::SlogDeterminantOpMaker,
ops::SlogDeterminantGradOpMaker<paddle::framework::OpDesc>, ops::SlogDeterminantGradOpMaker<paddle::framework::OpDesc>,
ops::SlogDeterminantGradOpMaker<paddle::imperative::OpBase>); ops::SlogDeterminantGradOpMaker<paddle::imperative::OpBase>,
SlogDeterminantInferShapeFunctor);
DECLARE_INFER_SHAPE_FUNCTOR(slogdeterminant_grad,
SlogDeterminantGradInferShapeFunctor,
PD_INFER_META(phi::GeneralUnaryGradInferMeta));
REGISTER_OPERATOR(slogdeterminant_grad, REGISTER_OPERATOR(slogdeterminant_grad,
ops::SlogDeterminantGradOp) // reuse det grad op ops::SlogDeterminantGradOp,
SlogDeterminantGradInferShapeFunctor) // reuse det grad op
REGISTER_OP_CPU_KERNEL(slogdeterminant,
ops::SlogDeterminantKernel<phi::CPUContext, float>,
ops::SlogDeterminantKernel<phi::CPUContext, double>);
REGISTER_OP_CPU_KERNEL(slogdeterminant_grad,
ops::SlogDeterminantGradKernel<phi::CPUContext, float>,
ops::SlogDeterminantGradKernel<phi::CPUContext, double>);
/* 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/fluid/operators/determinant_op.h"
#include "paddle/fluid/framework/op_registry.h"
namespace ops = paddle::operators;
namespace plat = paddle::platform;
REGISTER_OP_CUDA_KERNEL(
slogdeterminant,
ops::SlogDeterminantKernel<plat::CUDADeviceContext, float>,
ops::SlogDeterminantKernel<plat::CUDADeviceContext, double>);
REGISTER_OP_CUDA_KERNEL(
slogdeterminant_grad,
ops::SlogDeterminantGradKernel<plat::CUDADeviceContext, float>,
ops::SlogDeterminantGradKernel<plat::CUDADeviceContext, double>);
// 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.
#pragma once
#include <Eigen/Dense>
#include <Eigen/LU>
#include <algorithm>
#include <cmath>
#include <vector>
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/platform/enforce.h"
#include "paddle/fluid/platform/for_range.h"
#include "paddle/phi/kernels/complex_kernel.h"
#include "paddle/phi/kernels/elementwise_multiply_kernel.h"
#include "paddle/phi/kernels/full_kernel.h"
#include "paddle/phi/kernels/funcs/common_shape.h"
#include "paddle/phi/kernels/funcs/diag_functor.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/matrix_inverse.h"
#include "paddle/phi/kernels/funcs/unsqueeze.h"
#include "paddle/phi/kernels/impl/determinant_grad_kernel_impl.h"
#include "paddle/phi/kernels/impl/determinant_kernel_impl.h"
#include "paddle/phi/kernels/matmul_kernel.h"
#include "paddle/phi/kernels/transpose_kernel.h"
namespace paddle {
namespace operators {
using Tensor = framework::Tensor;
template <typename T>
T sign(T val) {
return static_cast<T>(T(0) < val) - (val < T(0));
}
template <typename T>
struct SlogDeterminantFunctor {
void operator()(const Tensor& input,
const framework::ExecutionContext ctx,
int64_t rank,
int64_t batch_count,
Tensor* output) {
std::vector<T> input_vec;
std::vector<T> sign_vec;
std::vector<T> log_vec;
std::vector<T> output_vec;
framework::TensorToVector(input, ctx.device_context(), &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<T> sub_vec(begin_iter,
end_iter); // get every square matrix data
typename phi::detail::EigenMatrix<T>::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());
framework::TensorFromVector(output_vec, output);
}
};
template <typename DeviceContext, typename T>
class SlogDeterminantKernel : public framework::OpKernel<T> {
public:
void Compute(const framework::ExecutionContext& context) const override {
auto* input = context.Input<framework::Tensor>("Input");
auto input_dim = vectorize(input->dims());
auto input_dim_size = input_dim.size();
auto* output = context.Output<framework::Tensor>("Out");
auto batch_count = phi::detail::GetBatchCount(input->dims());
VLOG(2) << "input dim:" << input->dims();
PADDLE_ENFORCE_GE(
input_dim_size,
2,
platform::errors::InvalidArgument(
"the input matrix dimension size should greater than 2."));
PADDLE_ENFORCE_EQ(input_dim[input_dim_size - 1],
input_dim[input_dim_size - 2],
platform::errors::InvalidArgument(
"the input matrix should be square matrix."));
auto rank = input_dim[input_dim_size - 1]; // square matrix length
SlogDeterminantFunctor<T>()(*input, context, rank, batch_count, output);
std::vector<int> output_dim_vec(input_dim.begin(), input_dim.end() - 2);
if (input_dim.size() == static_cast<size_t>(2)) {
// when input is a two-dimension matrix, The det value is a number.
output_dim_vec = {1};
}
output_dim_vec.insert(output_dim_vec.begin(),
2); // make the output dims as same as numpy
auto output_dims = phi::make_ddim(output_dim_vec);
output->Resize(output_dims);
VLOG(2) << "output dim:" << output->dims();
}
};
template <typename DeviceContext, typename T>
class SlogDeterminantGradKernel : public framework::OpKernel<T> {
public:
void Compute(const framework::ExecutionContext& context) const override {
auto& orig_dev_ctx = context.template device_context<DeviceContext>();
const auto* input = context.Input<framework::Tensor>("Input");
const auto* slogdet = context.Input<framework::Tensor>("Out");
const auto* grad =
context.Input<framework::Tensor>(framework::GradVarName("Out"));
auto* dslogdet =
context.Output<framework::Tensor>(framework::GradVarName("Input"));
PADDLE_ENFORCE_EQ(grad->dims()[0],
2,
platform::errors::InvalidArgument(
"The grad tensor of SlogDet should contain two"
" grad: sign and absslogdet, but here %ld.",
grad->dims()[0]));
if (input->dims().size() > 2) {
PADDLE_ENFORCE_EQ(
grad->dims().size() + 1,
input->dims().size(),
platform::errors::InvalidArgument(
"The grad tensor of slogdet dims size should 1 less than"
" input tensor's, but here differ %d",
input->dims().size() - grad->dims().size()));
}
auto& dev_ctx = static_cast<
const typename framework::ConvertToPhiContext<DeviceContext>::TYPE&>(
orig_dev_ctx);
// Check Whether the matrix is invertible
// (matrix A not invertible) == (absslogdet(A)=0)
auto slogdet_vec = slogdet->Split(1, 0);
auto absslogdet_val = slogdet_vec[0];
if (!phi::detail::CheckMatrixInvertible<
T,
typename framework::ConvertToPhiContext<DeviceContext>::TYPE>(
dev_ctx, &absslogdet_val)) {
// The matrix is not invertible
VLOG(3) << "The input matrix not invertible!";
dslogdet->Resize(input->dims());
phi::Full<T>(dev_ctx,
phi::vectorize(input->dims()),
std::numeric_limits<T>::quiet_NaN(),
dslogdet);
return;
}
// The matrix is invertible
// let sl|A| = SlogDeterminant(A)
// Ref to https://people.maths.ox.ac.uk/gilesm/files/NA-08-01.pdf
// we set dsl|A| = unsqueeze(dslA, [-1, -2]) *
// inverse(A).conj().transpose(-2, -1)
// First: inverse(A)
framework::Tensor inverse_A;
// A must be square matrices!
inverse_A.Resize(input->dims());
inverse_A.mutable_data<T>(context.GetPlace());
phi::funcs::MatrixInverseFunctor<DeviceContext, T> mat_inv;
mat_inv(orig_dev_ctx, *input, &inverse_A);
VLOG(3) << "inverse(A) dims: " << inverse_A.dims();
// Second: inverse(A).conj()
auto conj_inverse_A = phi::Conj<T>(dev_ctx, inverse_A);
VLOG(3) << "inverse(A).conj() dims: " << conj_inverse_A.dims();
// Third: inverse(A).conj().transpose(-2, -1)
framework::Tensor transpose_inverse_A =
phi::TransposeLast2Dim<T>(dev_ctx, conj_inverse_A);
VLOG(3) << "inverse(A).conj().transpose(-2, -1) dims: "
<< transpose_inverse_A.dims();
// Fourth: split grad value to [sign_grad, absslogdet_grad]
auto grad_vec = grad->Split(1, 0);
auto det_grad = grad_vec[1];
// remmove useless first dimension
int det_grad_size = det_grad.dims().size();
std::vector<int> det_grad_vec;
for (int i = 1; i < det_grad_size; ++i) {
det_grad_vec.emplace_back(det_grad.dims()[i]);
}
det_grad.Resize(det_grad.dims().reshape(det_grad_vec));
// Fifth: unsqueeze(dslA, [-1, -2])
auto unsqueeze1 = phi::funcs::Unsqueeze(det_grad, -1);
auto unsqueeze2 = phi::funcs::Unsqueeze(unsqueeze1, -2);
VLOG(3) << "unsqueezed(dslA, [-1, -2]) dims: " << unsqueeze2.dims();
// Finally: unsqueeze(dslA) * inverse(A)
auto res = phi::Multiply<T>(dev_ctx, unsqueeze2, transpose_inverse_A);
VLOG(3) << "unsqueeze(dslA) * inverse(A) dims: " << res.dims();
framework::TensorCopy(res, context.GetPlace(), dslogdet);
dslogdet->Resize(input->dims());
VLOG(3) << "dsl|A| dims: " << dslogdet->dims();
}
};
} // namespace operators
} // namespace paddle
...@@ -2029,6 +2029,15 @@ ...@@ -2029,6 +2029,15 @@
func : slice func : slice
backward : slice_grad backward : slice_grad
- api : slogdet
args : (Tensor x)
output : Tensor
infer_meta :
func : UnchangedInferMeta
kernel :
func : slogdeterminant
backward : slogdet_grad
# soft_shrink # soft_shrink
- api : soft_shrink - api : soft_shrink
args : (Tensor x, float lambda) args : (Tensor x, float lambda)
......
...@@ -1946,6 +1946,16 @@ ...@@ -1946,6 +1946,16 @@
backward : slice_double_grad backward : slice_double_grad
no_need_buffer : input no_need_buffer : input
- backward_api : slogdet_grad
forward : slogdet (Tensor x) -> Tensor(out)
args : (Tensor x, Tensor out, Tensor out_grad)
output : Tensor(x_grad)
infer_meta :
func : UnchangedInferMeta
param : [x]
kernel :
func : slogdeterminant_grad
- backward_api : soft_shrink_grad - backward_api : soft_shrink_grad
forward : soft_shrink (Tensor x, float lambda) -> Tensor(out) forward : soft_shrink (Tensor x, float lambda) -> Tensor(out)
args : (Tensor x, Tensor out_grad, float lambda) args : (Tensor x, Tensor out_grad, float lambda)
......
// 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/slogdeterminant_grad_kernel.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/impl/slogdeterminant_grad_kernel_impl.h"
PD_REGISTER_KERNEL(slogdeterminant_grad,
CPU,
ALL_LAYOUT,
phi::SlogDeterminantGradKernel,
float,
double) {}
// 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/slogdeterminant_kernel.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/impl/slogdeterminant_kernel_impl.h"
PD_REGISTER_KERNEL(slogdeterminant,
CPU,
ALL_LAYOUT,
phi::SlogDeterminantKernel,
float,
double) {}
// 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/slogdeterminant_grad_kernel.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/impl/slogdeterminant_grad_kernel_impl.h"
PD_REGISTER_KERNEL(slogdeterminant_grad,
GPU,
ALL_LAYOUT,
phi::SlogDeterminantGradKernel,
float,
double) {}
// 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/slogdeterminant_kernel.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/impl/slogdeterminant_kernel_impl.h"
PD_REGISTER_KERNEL(slogdeterminant,
GPU,
ALL_LAYOUT,
phi::SlogDeterminantKernel,
float,
double) {}
// 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 "paddle/phi/core/tensor_utils.h"
#include "paddle/phi/kernels/complex_kernel.h"
#include "paddle/phi/kernels/elementwise_multiply_kernel.h"
#include "paddle/phi/kernels/full_kernel.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/phi/kernels/funcs/matrix_inverse.h"
#include "paddle/phi/kernels/funcs/unsqueeze.h"
#include "paddle/phi/kernels/impl/determinant_grad_kernel_impl.h"
#include "paddle/phi/kernels/slogdeterminant_grad_kernel.h"
#include "paddle/phi/kernels/transpose_kernel.h"
namespace phi {
template <typename T, typename Context>
void SlogDeterminantGradKernel(const Context& dev_ctx,
const DenseTensor& x,
const DenseTensor& out,
const DenseTensor& out_grad,
DenseTensor* x_grad) {
PADDLE_ENFORCE_EQ(
out_grad.dims()[0],
2,
errors::InvalidArgument("The grad tensor of SlogDet should contain two"
" grad: sign and absslogdet, but here %ld.",
out_grad.dims()[0]));
if (x.dims().size() > 2) {
PADDLE_ENFORCE_EQ(
out_grad.dims().size() + 1,
x.dims().size(),
errors::InvalidArgument(
"The grad tensor of slogdet dims size should 1 less than"
" input tensor's, but here differ %d",
x.dims().size() - out_grad.dims().size()));
}
// Check Whether the matrix is invertible
// (matrix A not invertible) == (absslogdet(A)=0)
auto slogdet_vec = out.Split(1, 0);
auto absslogdet_val = slogdet_vec[0];
if (!detail::CheckMatrixInvertible<T, Context>(dev_ctx, &absslogdet_val)) {
// The matrix is not invertible
VLOG(3) << "The input matrix not invertible!";
x_grad->Resize(x.dims());
phi::Full<T>(dev_ctx,
phi::vectorize(x.dims()),
std::numeric_limits<T>::quiet_NaN(),
x_grad);
return;
}
// The matrix is invertible
// let sl|A| = SlogDeterminant(A)
// Ref to https://people.maths.ox.ac.uk/gilesm/files/NA-08-01.pdf
// we set dsl|A| = unsqueeze(dslA, [-1, -2]) *
// inverse(A).conj().transpose(-2, -1)
// First: inverse(A)
DenseTensor inverse_A;
// A must be square matrices!
inverse_A.Resize(x.dims());
dev_ctx.template Alloc<T>(&inverse_A);
phi::funcs::MatrixInverseFunctor<Context, T> mat_inv;
mat_inv(dev_ctx, x, &inverse_A);
VLOG(3) << "inverse(A) dims: " << inverse_A.dims();
// Second: inverse(A).conj()
auto conj_inverse_A = phi::Conj<T>(dev_ctx, inverse_A);
VLOG(3) << "inverse(A).conj() dims: " << conj_inverse_A.dims();
// Third: inverse(A).conj().transpose(-2, -1)
DenseTensor transpose_inverse_A =
phi::TransposeLast2Dim<T>(dev_ctx, conj_inverse_A);
VLOG(3) << "inverse(A).conj().transpose(-2, -1) dims: "
<< transpose_inverse_A.dims();
// Fourth: split grad value to [sign_grad, absslogdet_grad]
auto grad_vec = out_grad.Split(1, 0);
auto det_grad = grad_vec[1];
// remmove useless first dimension
int det_grad_size = det_grad.dims().size();
std::vector<int> det_grad_vec;
for (int i = 1; i < det_grad_size; ++i) {
det_grad_vec.emplace_back(det_grad.dims()[i]);
}
det_grad.Resize(det_grad.dims().reshape(det_grad_vec));
// Fifth: unsqueeze(dslA, [-1, -2])
auto unsqueeze1 = phi::funcs::Unsqueeze(det_grad, -1);
auto unsqueeze2 = phi::funcs::Unsqueeze(unsqueeze1, -2);
VLOG(3) << "unsqueezed(dslA, [-1, -2]) dims: " << unsqueeze2.dims();
// Finally: unsqueeze(dslA) * inverse(A)
auto res = phi::Multiply<T>(dev_ctx, unsqueeze2, transpose_inverse_A);
VLOG(3) << "unsqueeze(dslA) * inverse(A) dims: " << res.dims();
phi::Copy(dev_ctx, res, dev_ctx.GetPlace(), false, x_grad);
x_grad->Resize(x.dims());
VLOG(3) << "dsl|A| dims: " << x_grad->dims();
}
} // namespace phi
// 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 <algorithm>
#include <cmath>
#include <vector>
#include "paddle/fluid/framework/tensor_util.h"
#include "paddle/phi/core/enforce.h"
#include "paddle/phi/kernels/impl/determinant_kernel_impl.h"
#include "paddle/phi/kernels/slogdeterminant_kernel.h"
namespace phi {
template <typename T>
T sign(T val) {
return static_cast<T>(T(0) < val) - (val < T(0));
}
template <typename T, typename Context>
struct SlogDeterminantFunctor {
void operator()(const Context& dev_ctx,
const DenseTensor& input,
int64_t rank,
int64_t batch_count,
DenseTensor* output) {
std::vector<T> input_vec;
std::vector<T> sign_vec;
std::vector<T> log_vec;
std::vector<T> output_vec;
paddle::framework::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<T> sub_vec(begin_iter,
end_iter); // get every square matrix data
typename detail::EigenMatrix<T>::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());
paddle::framework::TensorFromVector(output_vec, output);
}
};
template <typename T, typename Context>
void SlogDeterminantKernel(const Context& dev_ctx,
const DenseTensor& x,
DenseTensor* out) {
auto input_dim = vectorize(x.dims());
auto input_dim_size = input_dim.size();
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 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<T, Context>()(dev_ctx, x, rank, batch_count, out);
std::vector<int> output_dim_vec(input_dim.begin(), input_dim.end() - 2);
if (input_dim.size() == static_cast<size_t>(2)) {
// when input is a two-dimension matrix, The det value is a number.
output_dim_vec = {1};
}
output_dim_vec.insert(output_dim_vec.begin(),
2); // make the output dims as same as numpy
auto output_dims = phi::make_ddim(output_dim_vec);
out->Resize(output_dims);
VLOG(2) << "output dim:" << out->dims();
}
} // namespace phi
// 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 "paddle/phi/core/dense_tensor.h"
namespace phi {
template <typename T, typename Context>
void SlogDeterminantGradKernel(const Context& dev_ctx,
const DenseTensor& x,
const DenseTensor& out,
const DenseTensor& out_grad,
DenseTensor* x_grad);
} // namespace phi
// 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 "paddle/phi/core/dense_tensor.h"
namespace phi {
template <typename T, typename Context>
void SlogDeterminantKernel(const Context& dev_ctx,
const DenseTensor& x,
DenseTensor* out);
} // namespace phi
// 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/core/compat/op_utils.h"
namespace phi {
KernelSignature SlogDeterminantGradOpArgumentMapping(
const ArgumentMappingContext& ctx) {
return KernelSignature(
"slogdeterminant_grad", {"Input", "Out", "Out@GRAD"}, {}, {"Input@GRAD"});
}
} // namespace phi
PD_REGISTER_ARG_MAPPING_FN(slogdeterminant_grad,
phi::SlogDeterminantGradOpArgumentMapping);
...@@ -104,15 +104,18 @@ class TestSlogDeterminantOp(OpTest): ...@@ -104,15 +104,18 @@ class TestSlogDeterminantOp(OpTest):
def setUp(self): def setUp(self):
self.op_type = "slogdeterminant" self.op_type = "slogdeterminant"
self.python_api = paddle.linalg.slogdet
self.init_data() self.init_data()
self.outputs = {'Out': self.target} self.outputs = {'Out': self.target}
def test_check_output(self): def test_check_output(self):
self.check_output() self.check_output(check_eager=True)
def test_check_grad(self): def test_check_grad(self):
# the slog det's grad value is always huge # the slog det's grad value is always huge
self.check_grad(['Input'], ['Out'], max_relative_error=0.1) self.check_grad(['Input'], ['Out'],
max_relative_error=0.1,
check_eager=True)
def init_data(self): def init_data(self):
np.random.seed(0) np.random.seed(0)
......
...@@ -1781,7 +1781,10 @@ def slogdet(x, name=None): ...@@ -1781,7 +1781,10 @@ def slogdet(x, name=None):
# [-0.98610914, -0.43010661, -0.10872950]]) # [-0.98610914, -0.43010661, -0.10872950]])
""" """
if paddle.in_dynamic_mode(): if in_dygraph_mode():
return _C_ops.final_state_slogdet(x)
elif paddle.in_dynamic_mode():
return _C_ops.slogdeterminant(x) return _C_ops.slogdeterminant(x)
check_dtype(x.dtype, 'Input', ['float32', 'float64'], 'slogdet') check_dtype(x.dtype, 'Input', ['float32', 'float64'], 'slogdet')
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册