Я бы попробовал блочную инверсию.
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. Внизу слева: 2x8. Справа внизу: 2x2. Инвертируйте 8x8, используя оптимизированный код инверсии 4x4. Остальное - это матричные продукты.
РЕДАКТИРОВАТЬ: Использование блоков 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 миллисекунд
Мне очень интересно посмотреть, сможет ли кто-нибудь еще оптимизировать это, так как у меня есть приложение с конечными элементами, которое инвертирует матрицу gazillion 10x10 (и да, мне нужны индивидуальные коэффициенты обратного преобразования, так что непосредственное решение линейной системы не всегда возможно) ,