Source code for mllm_shap.estimation.exact

"""Exact coalition enumeration estimator.

@domain estimation
@see [[knowledge/architecture/estimation]]
@tested-by [[tests/estimation/test_exact.py]]
"""

from collections.abc import Iterator
from typing import TYPE_CHECKING

from ..domain.coalition import CoalitionMask
from ..domain.players import PlayerSet
from ._shared import _SnapshotEstimator, mask_int_to_present

if TYPE_CHECKING:
    pass


[docs] class ExactEstimator(_SnapshotEstimator): """Enumerate all coalitions and delegate attribution to an estimand plugin.""" @property def name(self) -> str: """Return the registered estimator identifier.""" return "exact"
[docs] @staticmethod def num_coalitions(num_players: int) -> int: """Return the number of coalitions evaluated by exact enumeration (2^n).""" if num_players < 1: msg = "num_players must be at least 1" raise ValueError(msg) return 1 << num_players
[docs] @staticmethod def iter_mask_ints(num_players: int) -> Iterator[int]: """Lazily yield every coalition bitmask from empty to grand.""" yield from range(1 << num_players)
mask_int_to_present = staticmethod(mask_int_to_present)
[docs] def sample_masks( self, player_set: PlayerSet, budget_fraction: float, include_minimal_masks: bool, seed: int, ) -> Iterator[CoalitionMask]: """Yield every coalition mask including the empty and grand coalitions.""" if budget_fraction != 1.0: msg = "exact estimator requires budget_fraction == 1.0" raise ValueError(msg) _ = include_minimal_masks, seed return self.iter_masks(player_set)
[docs] @staticmethod def iter_masks(player_set: PlayerSet) -> Iterator[CoalitionMask]: """Yield every coalition mask including the empty and grand coalitions.""" num_players = player_set.num_players for mask_int in ExactEstimator.iter_mask_ints(num_players): present = mask_int_to_present(mask_int, num_players) yield CoalitionMask.from_sequence(present)