Source code for htfa._matching
"""Hungarian matching of factors by their centers."""
import numpy as np
from numpy.typing import NDArray
from scipy.optimize import linear_sum_assignment
from scipy.spatial.distance import cdist
Array = NDArray[np.float64]
[docs]
def match_factors(true_centers: Array, est_centers: Array) -> NDArray[np.intp]:
"""Match estimated factors to true ones with the Hungarian algorithm.
Parameters
----------
true_centers : ndarray, shape (K, D)
Ground-truth centers.
est_centers : ndarray, shape (K, D)
Estimated centers, in arbitrary order.
Returns
-------
perm : ndarray of int, shape (K,)
Permutation such that ``est_centers[perm][k]`` corresponds to
``true_centers[k]`` (minimum total squared Euclidean distance).
Examples
--------
>>> import numpy as np
>>> from htfa.synthetic import match_factors
>>> true = np.array([[0.0, 0.0], [5.0, 5.0]])
>>> est = np.array([[5.1, 4.9], [0.1, -0.1]])
>>> match_factors(true, est)
array([1, 0])
"""
cost = cdist(true_centers, est_centers, metric="sqeuclidean")
rows, cols = linear_sum_assignment(cost)
perm = np.empty(len(rows), dtype=np.intp)
perm[rows] = cols
return perm
def match_components(template_centers: Array, est_centers: Array) -> NDArray[np.intp]:
"""Match each factor's components to a template's, by their centers.
Parameters
----------
template_centers, est_centers : ndarray, shape (K, M, D)
Component centers of K factors with M components each, the factors
already matched (`match_factors`).
Returns
-------
perm : ndarray of int, shape (K, M)
``est_centers[k, perm[k]]`` corresponds to ``template_centers[k]``
(Hungarian, per factor).
Examples
--------
>>> import numpy as np
>>> from htfa._matching import match_components
>>> t = np.array([[[0.0, 0.0], [5.0, 5.0]]])
>>> match_components(t, t[:, ::-1])
array([[1, 0]])
"""
return np.stack(
[
match_factors(t, e)
for t, e in zip(template_centers, est_centers, strict=True)
]
)