Skip to content

check_clone_preserves_params

yohou.testing.check_clone_preserves_params(estimator)

Check clone() preserves shallow params and freshens nested estimators.

Family-agnostic generalization of check_clone_preserves_forecaster_params: the clone reproduces every get_params(deep=False) value, and nested sub-estimators (including (name, estimator) tuple lists) become fresh instances of the same type.

Parameters

Name Type Description Default
estimator BaseEstimator

Estimator instance to clone.

required

Raises

Type Description
AssertionError

If the clone has different parameter keys or values.

Source Code

Source code in src/yohou/testing/contract.py
def check_clone_preserves_params(estimator) -> None:
    """Check ``clone()`` preserves shallow params and freshens nested estimators.

    Family-agnostic generalization of ``check_clone_preserves_forecaster_params``:
    the clone reproduces every ``get_params(deep=False)`` value, and nested
    sub-estimators (including ``(name, estimator)`` tuple lists) become fresh
    instances of the same type.

    Parameters
    ----------
    estimator : BaseEstimator
        Estimator instance to clone.

    Raises
    ------
    AssertionError
        If the clone has different parameter keys or values.

    """
    name = type(estimator).__name__
    estimator_clone = clone(estimator)

    original = estimator.get_params(deep=False)
    cloned = estimator_clone.get_params(deep=False)

    assert set(original.keys()) == set(cloned.keys()), (
        f"{name}: clone() changed parameter keys: {set(cloned.keys())} vs {set(original.keys())}"
    )

    for key, orig_val in original.items():
        cloned_val = cloned[key]
        if orig_val is None:
            assert cloned_val is None, f"{name}: parameter {key!r} expected None, got {cloned_val!r}"
        elif isinstance(orig_val, list) and orig_val and isinstance(orig_val[0], tuple):
            # List of (name, estimator[, ...]) tuples (compositors).
            assert isinstance(cloned_val, list), f"{name}: parameter {key!r} expected list, got {type(cloned_val)}"
            assert len(cloned_val) == len(orig_val), f"{name}: parameter {key!r} changed length under clone()"
            for orig_item, cloned_item in zip(orig_val, cloned_val, strict=True):
                if not (isinstance(orig_item, tuple) and isinstance(cloned_item, tuple)):
                    continue
                assert orig_item[0] == cloned_item[0], f"{name}: component name changed under clone()"
                orig_est, cloned_est = orig_item[1], cloned_item[1]
                if isinstance(orig_est, BaseEstimator):
                    assert type(orig_est) is type(cloned_est), (
                        f"{name}: component {orig_item[0]!r} changed type under clone()"
                    )
        elif isinstance(orig_val, BaseEstimator):
            assert type(orig_val) is type(cloned_val), f"{name}: parameter {key!r} changed type under clone()"
        elif not _safe_equal(orig_val, cloned_val):
            raise AssertionError(f"{name}: parameter {key!r} expected {orig_val!r}, got {cloned_val!r}")