/* 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 #include "Eigen/Core" #include "Eigen/LU" #include "paddle/phi/backends/all_context.h" #include "paddle/phi/core/dense_tensor.h" #include "paddle/phi/core/enforce.h" namespace phi { namespace funcs { // for TransposeNormal static std::vector getNewAxis(const int b_rank) { std::vector axis_1 = {0}; std::vector axis_2 = {1, 0}; std::vector axis_3 = {0, 2, 1}; std::vector axis_4 = {0, 1, 3, 2}; std::vector axis_5 = {0, 1, 2, 4, 3}; std::vector axis_6 = {0, 1, 2, 3, 5, 4}; std::vector axis_7 = {0, 1, 2, 3, 4, 6, 5}; std::vector axis_8 = {0, 1, 2, 3, 4, 5, 7, 6}; std::vector axis_9 = {0, 1, 2, 3, 4, 5, 6, 8, 7}; switch (b_rank) { case 1: return axis_1; break; case 2: return axis_2; break; case 3: return axis_3; break; case 4: return axis_4; break; case 5: return axis_5; break; case 6: return axis_6; break; case 7: return axis_7; break; case 8: return axis_8; break; default: return axis_9; } } // for Resize static std::vector getNewDimsVec(const DDim& b_dims) { std::vector b_dims_vec = vectorize(b_dims); int size = b_dims_vec.size(); if (size >= 2) { // swap the last 2 elements in b_dims_vec int64_t temp = b_dims_vec[size - 1]; b_dims_vec[size - 1] = b_dims_vec[size - 2]; b_dims_vec[size - 2] = temp; return b_dims_vec; } PADDLE_ENFORCE_NE( b_dims_vec.empty(), true, common::errors::PreconditionNotMet( "The size of tensor b must not be %d after getting new dims", 0)); // if b_dims_vec.size() == 1, just return original vec return b_dims_vec; } template void compute_solve_eigen(const Context& dev_ctx, const DenseTensor& a, const DenseTensor& b, DenseTensor* out) { using Matrix = Eigen::Matrix; using EigenMatrixMap = Eigen::Map; using ConstEigenMatrixMap = Eigen::Map; // prepare for a const auto& a_mat_dims = a.dims(); const int a_rank = a_mat_dims.size(); int n = a_mat_dims[a_rank - 1]; int64_t a_batch_size = a_rank > 2 ? a.numel() / (static_cast(n) * n) : 1; // prepare for b const auto& b_mat_dims = b.dims(); const int b_rank = b_mat_dims.size(); int b_h = n; int b_w = b_mat_dims[b_rank - 1]; int64_t b_batch_size = b_rank > 2 ? b.numel() / (static_cast(b_h) * b_w) : 1; const T* a_ptr = a.data(); const T* b_ptr = b.data(); out->Resize(b_mat_dims); // make sure the out dims is right T* out_ptr = dev_ctx.template Alloc(out); if (a_batch_size == b_batch_size) { for (int64_t i = 0; i < a_batch_size; ++i) { ConstEigenMatrixMap a_mat(a_ptr + static_cast(i) * n * n, n, n); ConstEigenMatrixMap b_mat( b_ptr + static_cast(i) * b_h * b_w, b_h, b_w); EigenMatrixMap out_mat( out_ptr + static_cast(i) * b_h * b_w, b_h, b_w); Eigen::PartialPivLU lu; lu.compute(a_mat); const T min_abs_pivot = lu.matrixLU().diagonal().cwiseAbs().minCoeff(); PADDLE_ENFORCE_GT( min_abs_pivot, static_cast(0), common::errors::InvalidArgument("Input is not invertible.")); out_mat.noalias() = lu.solve(b_mat); } } else { PADDLE_ENFORCE_EQ(a_batch_size, b_batch_size, common::errors::InvalidArgument( "All input tensors must have the same rank.")); } } // only used for complex input template void SolveLinearSystem(T* matrix_data, T* rhs_data, T* out_data, int order, int rhs_cols, int batch) { using Treal = typename Eigen::NumTraits::Real; // cast paddle::complex into std::complex std::complex* matrix_data_ = reinterpret_cast*>(matrix_data); std::complex* rhs_data_ = reinterpret_cast*>(rhs_data); std::complex* out_data_ = reinterpret_cast*>(out_data); using Matrix = Eigen::Matrix, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>; using InputMatrixMap = Eigen::Map; using OutputMatrixMap = Eigen::Map; for (int i = 0; i < batch; ++i) { auto input_matrix = InputMatrixMap(matrix_data_ + i * order * order, order, order); auto input_rhs = InputMatrixMap(rhs_data_ + i * order * rhs_cols, order, rhs_cols); auto output = OutputMatrixMap(out_data_ + i * order * rhs_cols, order, rhs_cols); Eigen::PartialPivLU lu_decomposition(order); lu_decomposition.compute(input_matrix); const Treal min_abs_piv = lu_decomposition.matrixLU().diagonal().cwiseAbs().minCoeff(); PADDLE_ENFORCE_GT(min_abs_piv, Treal(0), common::errors::InvalidArgument( "Something's wrong with SolveLinearSystem. ")); output = lu_decomposition.solve(input_rhs); } } template class MatrixSolveFunctor { public: void operator()(const Context& dev_ctx, const DenseTensor& a, const DenseTensor& b, DenseTensor* out); }; } // namespace funcs } // namespace phi