-
Notifications
You must be signed in to change notification settings - Fork 197
Open
Labels
ideaProposition of an idea and opening an issue to discuss itProposition of an idea and opening an issue to discuss ittopic: algorithmssearching and sorting, merging, ...searching and sorting, merging, ...topic: mathematicslinear algebra, sparse matrices, special functions, FFT, random numbers, statistics, ...linear algebra, sparse matrices, special functions, FFT, random numbers, statistics, ...
Description
Motivation
I would like to propose to add the the Kabsh-Umeyama algorithm (https://en.wikipedia.org/wiki/Kabsch_algorithm) in a (yet to be) stdlib_spatial module for vector alignment purposes.
A first API implementation combining several approaches including the use of weights:
module test
use stdlib_kinds, only: dp
use stdlib_intrinsics, only: stdlib_sum
use stdlib_linalg, only: svd, det, eye, trace
contains
subroutine kabsh_umeyama( R, t, c, lrms, d, N, P, Q, W )
integer, parameter :: wp = dp
integer, intent(in) :: d, N
real(wp), intent(out):: R(d,d) !> Rotation matrix
real(wp), intent(out):: t(d) !> Translation vector
real(wp), intent(out):: c !> Scaling factor
real(wp), intent(out):: lrms !> least root mean square
real(wp), intent(in) :: P(d,N) !> Reference point cloud
real(wp), intent(in) :: Q(d,N) !> Target point cloud
real(wp), intent(in), optional :: W(N) !> Weights
! -- Internal Variables
integer :: i, j, point
real(wp) :: centroid_P(d), centroid_Q(d)
real(wp) :: covariance(d,d), S(d), U(d,d), Vt(d,d), B(d,d)
real(wp) :: vec(d), variance_p, vp, vq, isum_w
!---------------------------------------
! Compute centroids
isum_w = 1._wp / N
if(present(W)) isum_w = 1._wp / stdlib_sum(W)
centroid_P = 0._wp; centroid_Q = 0._wp
if(present(W))then
do concurrent( point = 1:N )
centroid_P(1:d) = centroid_P(1:d) + P(1:d,point)*W(point)
centroid_Q(1:d) = centroid_Q(1:d) + Q(1:d,point)*W(point)
end do
else
do concurrent( point = 1:N )
centroid_P(1:d) = centroid_P(1:d) + P(1:d,point)
centroid_Q(1:d) = centroid_Q(1:d) + Q(1:d,point)
end do
end if
centroid_P = centroid_P * isum_w
centroid_Q = centroid_Q * isum_w
! Compute the covariance matrix
covariance = 0._wp; variance_p = 0._wp
if(present(W))then
do concurrent( point = 1:N, j = 1:d, i = 1:d )
vp = P(i,point)-centroid_P(i)
vq = Q(j,point)-centroid_Q(j)
covariance(i,j) = covariance(i,j) + vp*vq * W(point)
variance_p = variance_p + vp**2
end do
else
do concurrent( point = 1:N, j = 1:d, i = 1:d )
vp = P(i,point)-centroid_P(i)
vq = Q(j,point)-centroid_Q(j)
covariance(i,j) = covariance(i,j) + vp*vq
variance_p = variance_p + vp**2
end do
end if
covariance = covariance * isum_w
variance_p = variance_p * isum_w / d
! SVD
call svd(covariance, S, U, Vt )
! Optimal rotation
R = matmul( U , Vt )
! Validate right-handed coordinate system
B = eye(d,d)
B(d,d) = sign( 1._wp, det(R))
R = matmul( U , matmul( B , Vt ) )
c = variance_p / (sum(S(1:d-1))+B(d,d)*S(d))
! Optimal translation (aligning Q to P)
t = centroid_P - c * matmul(R , centroid_Q )
! RMSD
lrms = 0._wp
do concurrent( point = 1:N )
vec(1:d) = t(1:d) + c * matmul(R , Q(1:d,point) )
vec(1:d) = vec(1:d) - P(1:d,point)
lrms = lrms + dot_product(vec,vec)
end do
lrms = sqrt( lrms * isum_w )
end subroutine
end module
program main
use test
integer, parameter :: wp = 8
integer, parameter :: d = 2
real(wp), allocatable :: P(:,:)
real(wp), allocatable :: Q(:,:)
real(wp) :: R(d,d), t(d), vec(d), c, lrms
integer :: i, N
N = 7
P = reshape( &
[23._wp, 178._wp,&
66._wp, 173._wp,&
88._wp, 187._wp,&
119._wp, 202._wp,&
122._wp, 229._wp,&
170._wp, 232._wp,&
179._wp, 199._wp] , [d,N] )
Q = reshape( &
[232._wp, 38._wp,&
208._wp, 32._wp,&
181._wp, 31._wp,&
155._wp, 45._wp,&
142._wp, 33._wp,&
121._wp, 59._wp,&
139._wp, 69._wp] , [d,N] )
call kabsh_umeyama(R, t, c, lrms, d, N, P, Q)
print *, 'RMSD :', lrms
print *, 'Translation :', t
print *, 'Scaling :', c
print *, 'Rotation :'
do i = 1, d
print *, R(i,:)
end do
end programgives:
LRMS : 16.242818369005477
Translation : 271.33459510449791 396.07800316838268
Scaling : 1.4616613091002035
Rotation :
-0.81034281019830057 0.58595608193782001
-0.58595608193782023 -0.81034281019830057 Prior Art
- "Aligning point patterns with Kabsch–Umeyama algorithm": https://zpl.fi/aligning-point-patterns-with-kabsch-umeyama-algorithm/?utm_source=chatgpt.com
- SciPy: https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.align_vectors.html
- Implementations in (Numpy, Torch, Jax): https://hunterheidenreich.com/posts/kabsch-algorithm/
- MATLAB : https://www.mathworks.com/matlabcentral/fileexchange/25746-kabsch-algorithm?utm_source=chatgpt.com
- Eigen (C++) : https://libeigen.gitlab.io/eigen/docs-nightly/Umeyama_8h_source.html?utm_source=chatgpt.com
- R : https://rdrr.io/cran/pracma/man/procrustes.html?utm_source=chatgpt.com
Additional Information
No response
Metadata
Metadata
Assignees
Labels
ideaProposition of an idea and opening an issue to discuss itProposition of an idea and opening an issue to discuss ittopic: algorithmssearching and sorting, merging, ...searching and sorting, merging, ...topic: mathematicslinear algebra, sparse matrices, special functions, FFT, random numbers, statistics, ...linear algebra, sparse matrices, special functions, FFT, random numbers, statistics, ...