Я б спробував блокадно інверсію.
https://en.wikipedia.org/wiki/Invertible_matrix#Blockwise_inversion
Eigen використовує оптимізовану процедуру для обчислення зворотної матриці 4x4, що, мабуть, найкраще, що ви збираєтеся отримати. Спробуйте використовувати це якомога більше.
http://www.eigen.tuxfamily.org/dox/Inverse__SSE_8h_source.html
Зліва вгорі: 8х8. Праворуч вгорі: 8х2. Знизу ліворуч: 2х8. Праворуч знизу: 2х2. Інвертуйте 8x8 за допомогою оптимізованого коду інверсії 4x4. Решта - матричні вироби.
EDIT: Використання блоків 6x6, 6x4, 4x6 та 4x4 виявилося трохи швидше, ніж те, що я описав вище.
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;
}
Ось результати пробігу однієї лавки з використанням мільйона Eigen::Matrix<double,10,10>::Random()
матриць та Eigen::Matrix<double,10,1>::Random()
векторів. У всіх моїх тестах моя інверсія завжди швидша. Моя розв'язка включає в себе обчислення зворотного, а потім множення його на вектор. Іноді це швидше, ніж Ейген, іноді - ні. Моя методика розмітки на лавці може бути помилковою (не вимкнено турбо-підвищення тощо). Також випадкові функції Ейгена можуть не відображати реальних даних.
- Часткове зворотне часткове враження: 3036 мілісекунд
- Мій зворотній з 8x8 верхнього блоку: 1638 мілісекунд
- Мій зворотній з 6x6 верхнього блоку: 1234 мілісекунди
- Часткове вирішення власного відкидання: 1791 мілісекунд
- Моє рішення з верхнім блоком 8x8: 1739 мілісекунд
- Моє рішення з 6x6 верхнього блоку: 1286 мілісекунд
Мені дуже цікаво дізнатись, чи може хтось додатково оптимізувати це, оскільки у мене є додаток з кінцевими елементами, який інвертує матриці 10х10 gazillion (і так, мені потрібні індивідуальні коефіцієнти зворотного, тому безпосередньо рішення лінійної системи не завжди є варіантом) .