Intentaría la inversión en bloque.
https://en.wikipedia.org/wiki/Invertible_matrix#Blockwise_inversion
Eigen utiliza una rutina optimizada para calcular el inverso de una matriz 4x4, que probablemente sea lo mejor que obtendrá. Intenta usar eso tanto como sea posible.
http://www.eigen.tuxfamily.org/dox/Inverse__SSE_8h_source.html
Arriba a la izquierda: 8x8. Arriba a la derecha: 8x2. Abajo a la izquierda: 2x8. Abajo a la derecha: 2x2. Invierta el 8x8 utilizando el código de inversión 4x4 optimizado. El resto son productos matriciales.
EDITAR: Usar bloques 6x6, 6x4, 4x6 y 4x4 ha demostrado ser un poco más rápido de lo que describí anteriormente.
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;
}
Aquí están los resultados de una ejecución de referencia utilizando un millón de Eigen::Matrix<double,10,10>::Random()
matrices y Eigen::Matrix<double,10,1>::Random()
vectores. En todas mis pruebas, mi inverso es siempre más rápido. Mi rutina de resolución implica calcular el inverso y luego multiplicarlo por un vector. A veces es más rápido que Eigen, a veces no. Mi método de marcado de banco puede ser defectuoso (no desactivó el turbo boost, etc.). Además, las funciones aleatorias de Eigen pueden no ser representativas de datos reales.
- Eigen pivote parcial inverso: 3036 milisegundos
- Mi inverso con bloque superior de 8x8: 1638 milisegundos
- Mi inverso con bloque superior 6x6: 1234 milisegundos
- Eigen pivote parcial resolver: 1791 milisegundos
- Mi solución con el bloque superior de 8x8: 1739 milisegundos
- Mi solución con el bloque superior 6x6: 1286 milisegundos
Estoy muy interesado en ver si alguien puede optimizar esto aún más, ya que tengo una aplicación de elementos finitos que invierte un millón de matrices de 10x10 (y sí, necesito coeficientes individuales de la inversa, por lo que resolver directamente un sistema lineal no siempre es una opción) .