Eu tentaria inversão em blocos.
https://en.wikipedia.org/wiki/Invertible_matrix#Blockwise_inversion
Eigen usa uma rotina otimizada para calcular o inverso de uma matriz 4x4, que é provavelmente o melhor que você obterá. Tente usar isso o máximo possível.
http://www.eigen.tuxfamily.org/dox/Inverse__SSE_8h_source.html
Superior esquerdo: 8x8. Superior direito: 8x2. Parte inferior esquerda: 2x8. Em baixo à direita: 2x2. Inverta o 8x8 usando o código de inversão 4x4 otimizado. O resto são produtos matriciais.
EDIT: O uso de blocos 6x6, 6x4, 4x6 e 4x4 mostrou ser um pouco mais rápido do que o que descrevi acima.
using namespace Eigen;
template<typename Scalar, int tl_size, int br_size>
Matrix<Scalar, tl_size + br_size, tl_size + br_size> blockwise_inversion(const Matrix<Scalar, tl_size, tl_size>& A, const Matrix<Scalar, tl_size, br_size>& B, const Matrix<Scalar, br_size, tl_size>& C, const Matrix<Scalar, br_size, br_size>& D)
{
Matrix<Scalar, tl_size + br_size, tl_size + br_size> result;
Matrix<Scalar, tl_size, tl_size> A_inv = A.inverse().eval();
Matrix<Scalar, br_size, br_size> DCAB_inv = (D - C * A_inv * B).inverse();
result.topLeftCorner<tl_size, tl_size>() = A_inv + A_inv * B * DCAB_inv * C * A_inv;
result.topRightCorner<tl_size, br_size>() = -A_inv * B * DCAB_inv;
result.bottomLeftCorner<br_size, tl_size>() = -DCAB_inv * C * A_inv;
result.bottomRightCorner<br_size, br_size>() = DCAB_inv;
return result;
}
template<typename Scalar, int tl_size, int br_size>
Matrix<Scalar, tl_size + br_size, tl_size + br_size> my_inverse(const Matrix<Scalar, tl_size + br_size, tl_size + br_size>& mat)
{
const Matrix<Scalar, tl_size, tl_size>& A = mat.topLeftCorner<tl_size, tl_size>();
const Matrix<Scalar, tl_size, br_size>& B = mat.topRightCorner<tl_size, br_size>();
const Matrix<Scalar, br_size, tl_size>& C = mat.bottomLeftCorner<br_size, tl_size>();
const Matrix<Scalar, br_size, br_size>& D = mat.bottomRightCorner<br_size, br_size>();
return blockwise_inversion<Scalar,tl_size,br_size>(A, B, C, D);
}
template<typename Scalar>
Matrix<Scalar, 10, 10> invert_10_blockwise_8_2(const Matrix<Scalar, 10, 10>& input)
{
Matrix<Scalar, 10, 10> result;
const Matrix<Scalar, 8, 8>& A = input.topLeftCorner<8, 8>();
const Matrix<Scalar, 8, 2>& B = input.topRightCorner<8, 2>();
const Matrix<Scalar, 2, 8>& C = input.bottomLeftCorner<2, 8>();
const Matrix<Scalar, 2, 2>& D = input.bottomRightCorner<2, 2>();
Matrix<Scalar, 8, 8> A_inv = my_inverse<Scalar, 4, 4>(A);
Matrix<Scalar, 2, 2> DCAB_inv = (D - C * A_inv * B).inverse();
result.topLeftCorner<8, 8>() = A_inv + A_inv * B * DCAB_inv * C * A_inv;
result.topRightCorner<8, 2>() = -A_inv * B * DCAB_inv;
result.bottomLeftCorner<2, 8>() = -DCAB_inv * C * A_inv;
result.bottomRightCorner<2, 2>() = DCAB_inv;
return result;
}
template<typename Scalar>
Matrix<Scalar, 10, 10> invert_10_blockwise_6_4(const Matrix<Scalar, 10, 10>& input)
{
Matrix<Scalar, 10, 10> result;
const Matrix<Scalar, 6, 6>& A = input.topLeftCorner<6, 6>();
const Matrix<Scalar, 6, 4>& B = input.topRightCorner<6, 4>();
const Matrix<Scalar, 4, 6>& C = input.bottomLeftCorner<4, 6>();
const Matrix<Scalar, 4, 4>& D = input.bottomRightCorner<4, 4>();
Matrix<Scalar, 6, 6> A_inv = my_inverse<Scalar, 4, 2>(A);
Matrix<Scalar, 4, 4> DCAB_inv = (D - C * A_inv * B).inverse().eval();
result.topLeftCorner<6, 6>() = A_inv + A_inv * B * DCAB_inv * C * A_inv;
result.topRightCorner<6, 4>() = -A_inv * B * DCAB_inv;
result.bottomLeftCorner<4, 6>() = -DCAB_inv * C * A_inv;
result.bottomRightCorner<4, 4>() = DCAB_inv;
return result;
}
Aqui estão os resultados de uma execução de benchmark usando um milhão de Eigen::Matrix<double,10,10>::Random()
matrizes e Eigen::Matrix<double,10,1>::Random()
vetores. Em todos os meus testes, meu inverso é sempre mais rápido. Minha rotina de resolução envolve calcular o inverso e depois multiplicá-lo por um vetor. Às vezes é mais rápido que Eigen, às vezes não. Meu método de marcação de bancada pode ter falhas (não desativou o turbo boost, etc.). Além disso, as funções aleatórias de Eigen podem não representar dados reais.
- Pivô parcial do próprio inverso: 3036 milissegundos
- Meu inverso com bloco superior de 8x8: 1638 milissegundos
- Meu inverso com bloco superior 6x6: 1234 milissegundos
- Solução de pivô parcial de Eigen: 1791 milissegundos
- Minha resolução com bloco superior 8x8: 1739 milissegundos
- Minha resolução com bloco superior 6x6: 1286 milissegundos
Estou muito interessado em ver se alguém pode otimizar isso ainda mais, pois tenho um aplicativo de elementos finitos que inverte um zilhão de matrizes 10x10 (e sim, preciso de coeficientes individuais do inverso, portanto, resolver diretamente um sistema linear nem sempre é uma opção) .