# =============================================================================
# @author: Shuo Zhou, The University of Sheffield
# =============================================================================
"""Maximum Independence Domain Adaptation (MIDA) implementation."""
from numbers import Real
from numpy.linalg import multi_dot
from sklearn.utils._param_validation import Interval
from kalelinear.transformer._base import _num_features, BaseKernelDomainTransformer
from kalelinear.utils import centered_kernel_matrix, centering_matrix
[docs]
class MIDA(BaseKernelDomainTransformer):
"""Maximum Independence Domain Adaptation (MIDA).
MIDA learns a covariate-invariant feature space by maximizing the
Hilbert-Schmidt independence criterion (HSIC) with respect to the
provided covariates.
``covariates`` are required during :meth:`fit`. With
``covariate_encoder=None``, they must already be numeric and shaped as
``(n_samples,)`` or ``(n_samples, n_covariates)``. Use
``covariate_encoder="onehot"`` to fit a one-hot encoder on raw
categorical covariates before adaptation. During :meth:`transform`,
covariates are only required when ``augment`` is ``"pre"`` or ``"post"``.
"""
_parameter_constraints: dict = {
**BaseKernelDomainTransformer._parameter_constraints,
"mu": [Interval(Real, 0, None, closed="neither")],
"eta": [Interval(Real, 0, None, closed="neither")],
}
def __init__(
self,
n_components=None,
mu=1.0,
eta=1.0,
ignore_y=False,
augment=None,
kernel="linear",
gamma=None,
degree=3,
coef0=1,
kernel_params=None,
covariate_encoder=None,
alpha=1,
fit_inverse_transform=False,
eigen_solver="auto",
tol=0,
max_iter=None,
iterated_power="auto",
remove_zero_eig=False,
scale_components=False,
random_state=None,
copy=True,
n_jobs=None,
):
self.mu = mu
self.eta = eta
super().__init__(
n_components=n_components,
ignore_y=ignore_y,
augment=augment,
kernel=kernel,
gamma=gamma,
degree=degree,
coef0=coef0,
kernel_params=kernel_params,
covariate_encoder=covariate_encoder,
alpha=alpha,
fit_inverse_transform=fit_inverse_transform,
eigen_solver=eigen_solver,
tol=tol,
max_iter=max_iter,
iterated_power=iterated_power,
remove_zero_eig=remove_zero_eig,
scale_components=scale_components,
random_state=random_state,
copy=copy,
n_jobs=n_jobs,
)
def _requires_covariates(self):
return True
def _make_eigenproblem(self, x_kernel_matrix, context):
h = centering_matrix(_num_features(x_kernel_matrix), x_kernel_matrix.dtype)
y_kernel_matrix = centered_kernel_matrix(context.y_encoded, n_jobs=self.n_jobs)
covariate_kernel = centered_kernel_matrix(context.covariates_fit, n_jobs=self.n_jobs)
return multi_dot(
(x_kernel_matrix, self.mu * h + self.eta * y_kernel_matrix - covariate_kernel, x_kernel_matrix)
)