Solve Weighted Procrustes Rotation Problem
Source:R/weighted_procrustes.R
solve_procrustes_rotation_weighted.Rd
Finds the optimal rotation matrix `R` that aligns a source matrix `A_source` to a target matrix `T_target`, minimizing `|| Omega (A_source R - T_target) ||_F^2`, where `Omega` is a diagonal weighting matrix.
Usage
solve_procrustes_rotation_weighted(
A_source,
T_target,
m_parcel_rows,
m_task_rows,
omega_mode = "fixed",
fixed_omega_weights = NULL,
reliability_scores = NULL,
scale_omega_trace = TRUE
)
Arguments
- A_source
The numeric source matrix (e.g., augmented anchors for one subject), dimensions `N x k` (N = total anchors, k = dimensions).
- T_target
The numeric target matrix (e.g., augmented template), dimensions `N x k`.
- m_parcel_rows
Integer, number of rows in `A_source` (and `T_target`) corresponding to parcel anchors. Assumed to be the first `m_parcel_rows`.
- m_task_rows
Integer, number of rows in `A_source` (and `T_target`) corresponding to task condition anchors. Assumed to be after parcel rows.
- omega_mode
Character string, mode for determining weights: `"fixed"` (default) or `"adaptive"`.
- fixed_omega_weights
List, used if `omega_mode == "fixed"`. E.g., `list(parcel = 1.0, condition = 0.5)`. If `NULL`, defaults to this.
- reliability_scores
Numeric vector of length `m_task_rows`, containing reliability scores (e.g., R^2_p) for each task condition. Required and used only if `omega_mode == "adaptive"`.
- scale_omega_trace
Logical, whether to rescale the diagonal `Omega` matrix so that `sum(diag(Omega)) == N`. Default `TRUE`.
Details
Internally computes the weighted cross-product using `crossprod(A_source, T_target * omega_diag_vector)` to avoid creating separate weighted matrices for `A_source` and `T_target`.
When the spectral dimension `k` is 1, the SVD-based determinant correction is unnecessary. In this case the rotation is simply the sign of the scalar cross-product `M`, i.e. `R <- matrix(sign(M), 1, 1)`.
Examples
N_parcels <- 5
N_tasks <- 3
N_total <- N_parcels + N_tasks
k_dims <- 4
A <- matrix(rnorm(N_total * k_dims), N_total, k_dims)
T_true <- matrix(rnorm(N_total * k_dims), N_total, k_dims)
R_true <- svd(matrix(rnorm(k_dims*k_dims),k_dims,k_dims))$u %*%
diag(sample(c(1,1,1,-1),k_dims,replace=TRUE)) %*%
svd(matrix(rnorm(k_dims*k_dims),k_dims,k_dims))$v
A_rotated_perfect <- T_true %*% t(R_true) # A_rotated_perfect R_true should be T_true
# Fixed weights (default)
R_fixed <- solve_procrustes_rotation_weighted(A_rotated_perfect, T_true,
N_parcels, N_tasks)
# print(all.equal(T_true %*% t(R_fixed), A_rotated_perfect)) # Check alignment
print(sum((A_rotated_perfect %*% R_fixed - T_true)^2)) # Should be small
#> [1] 9.915003
# Adaptive weights (example with dummy reliabilities)
set.seed(123)
rel_scores <- runif(N_tasks, -0.2, 0.8)
R_adaptive <- solve_procrustes_rotation_weighted(A_rotated_perfect, T_true,
N_parcels, N_tasks,
omega_mode = "adaptive",
reliability_scores = rel_scores)
print(sum((A_rotated_perfect %*% R_adaptive - T_true)^2)) # Should be small
#> [1] 17.43792
# No task rows
R_no_task <- solve_procrustes_rotation_weighted(A[1:N_parcels,], T_true[1:N_parcels,],
N_parcels, 0)