Vorrei provare l'inversione a blocchi.
https://en.wikipedia.org/wiki/Invertible_matrix#Blockwise_inversion
Eigen utilizza una routine ottimizzata per calcolare l'inverso di una matrice 4x4, che è probabilmente la migliore che otterrai. Prova a usarlo il più possibile.
http://www.eigen.tuxfamily.org/dox/Inverse__SSE_8h_source.html
In alto a sinistra: 8x8. In alto a destra: 8x2. In basso a sinistra: 2x8. In basso a destra: 2x2. Invertire 8x8 usando il codice di inversione 4x4 ottimizzato. Il resto sono prodotti a matrice.
EDIT: l'utilizzo dei blocchi 6x6, 6x4, 4x6 e 4x4 ha dimostrato di essere un po 'più veloce di quello che ho descritto sopra.
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;
}
Ecco i risultati di una corsa di riferimento usando un milione di Eigen::Matrix<double,10,10>::Random()
matrici e Eigen::Matrix<double,10,1>::Random()
vettori. In tutti i miei test, il mio inverso è sempre più veloce. La mia routine di risoluzione prevede il calcolo dell'inverso e quindi la sua moltiplicazione per un vettore. A volte è più veloce di Eigen, a volte no. Il mio metodo di marcatura al banco potrebbe essere difettoso (non disabilita il turbo boost, ecc.). Inoltre, le funzioni casuali di Eigen potrebbero non rappresentare dati reali.
- Inverso del perno parziale di Eigen: 3036 millisecondi
- Il mio inverso con blocco superiore 8x8: 1638 millisecondi
- Il mio inverso con blocco superiore 6x6: 1234 millisecondi
- Risoluzione pivot parziale di Eigen: 1791 millisecondi
- La mia soluzione con blocco superiore 8x8: 1739 millisecondi
- La mia soluzione con blocco superiore 6x6: 1286 millisecondi
Sono molto interessato a vedere se qualcuno può ottimizzarlo ulteriormente, poiché ho un'applicazione ad elementi finiti che inverte una matrice di gazillion 10x10 (e sì, ho bisogno di coefficienti individuali dell'inverso, quindi risolvere direttamente un sistema lineare non è sempre un'opzione) .