나는 블록 단위의 역전을 시도 할 것입니다.
https://en.wikipedia.org/wiki/Invertible_matrix#Blockwise_inversion
Eigen은 최적화 된 루틴을 사용하여 4x4 행렬의 역수를 계산합니다. 가능한 많이 사용하십시오.
http://www.eigen.tuxfamily.org/dox/Inverse__SSE_8h_source.html
왼쪽 상단 : 8x8 오른쪽 상단 : 8x2. 왼쪽 하단 : 2x8 오른쪽 하단 : 2x2. 최적화 된 4x4 반전 코드를 사용하여 8x8을 반전시킵니다. 나머지는 매트릭스 제품입니다.
편집 : 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 행렬을 뒤집는 유한 요소 응용 프로그램을 가지고 있기 때문에 누군가가 이것을 더 최적화 할 수 있는지에 매우 관심이 있습니다. .