Skip to content

Commit f2f7466

Browse files
Merge pull request #341 from aai-institute/feature/shapley-games
Toy Shapley games
2 parents 3e7b4c8 + 023040f commit f2f7466

20 files changed

+1690
-772
lines changed

.test_durations

Lines changed: 696 additions & 319 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@
22

33
## Unreleased
44

5+
### Added
6+
7+
- Implement new method: `EkfacInfluence`
8+
[PR #451](https://github.com/aai-institute/pyDVL/issues/451)
9+
- New notebook to showcase ekfac for LLMs
10+
[PR #483](https://github.com/aai-institute/pyDVL/pull/483)
11+
- Implemented exact games in Castro et al. 2009 and 2017
12+
[PR #341](https://github.com/appliedAI-Initiative/pyDVL/pull/341)
13+
514
### Fixed
615

716
- Bug in using `DaskInfluenceCalcualator` with `TorchnumpyConverter`
817
for single dimensional arrays [PR #485](https://github.com/aai-institute/pyDVL/pull/485)
918
- Fix implementations of `to` methods of `TorchInfluenceFunctionModel` implementations
1019
[PR #487](https://github.com/aai-institute/pyDVL/pull/487)
11-
- Implement new method: `EkfacInfluence`
12-
[PR #476](https://github.com/aai-institute/pyDVL/pull/476)
13-
- New notebook to showcase ekfac for LLMs
14-
[PR #483](https://github.com/aai-institute/pyDVL/pull/483)
20+
- Fixed bug with checking for converged values in semivalues
21+
[PR #341](https://github.com/appliedAI-Initiative/pyDVL/pull/341)
1522

1623
## 0.8.0 - 🆕 New interfaces, scaling computation, bug fixes and improvements 🎁
1724

CONTRIBUTING.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,12 @@ runs](#skipping-ci-runs)).
343343
3. We split the tests based on their duration into groups and run them in parallel.
344344

345345
For that we use [pytest-split](https://jerry-git.github.io/pytest-split)
346-
to first store the duration of all tests with `pytest --store-durations pytest --slow-tests`
346+
to first store the duration of all tests with
347+
`tox -e tests -- --store-durations --slow-tests`
347348
in a `.test_durations` file.
349+
350+
Alternatively, we case use pytest directly
351+
`pytest --store-durations --slow-tests`.
348352

349353
> **Note** This does not have to be done each time a new test or test case
350354
> is added. For new tests and test cases pytes-split assumes
@@ -359,11 +363,14 @@ runs](#skipping-ci-runs)).
359363
Then we can have as many splits as we want:
360364

361365
```shell
362-
pytest --splits 3 --group 1
363-
pytest --splits 3 --group 2
364-
pytest --splits 3 --group 3
366+
tox -e tests -- --splits 3 --group 1
367+
tox -e tests -- --splits 3 --group 2
368+
tox -e tests -- --splits 3 --group 3
365369
```
366370

371+
Alternatively, we case use pytest directly
372+
`pytest --splits 3 ---group 1`.
373+
367374
Each one of these commands should be run in a separate shell/job
368375
to run the test groups in parallel and decrease the total runtime.
369376

src/pydvl/utils/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
]
2424

2525
IndexT = TypeVar("IndexT", bound=np.int_)
26-
NameT = TypeVar("NameT", bound=np.object_)
26+
NameT = TypeVar("NameT", np.object_, np.int_)
2727
R = TypeVar("R", covariant=True)
2828
Seed = Union[int, Generator]
2929

src/pydvl/utils/utility.py

Lines changed: 1 addition & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from pydvl.utils.score import Scorer
3939
from pydvl.utils.types import SupervisedModel
4040

41-
__all__ = ["Utility", "DataUtilityLearning", "MinerGameUtility", "GlovesGameUtility"]
41+
__all__ = ["Utility", "DataUtilityLearning"]
4242

4343
logger = logging.getLogger(__name__)
4444

@@ -356,120 +356,3 @@ def __call__(self, indices: Iterable[int]) -> float:
356356
def data(self) -> Dataset:
357357
"""Returns the wrapped utility's [Dataset][pydvl.utils.dataset.Dataset]."""
358358
return self.utility.data
359-
360-
361-
class MinerGameUtility(Utility):
362-
r"""Toy game utility that is used for testing and demonstration purposes.
363-
364-
Consider a group of n miners, who have discovered large bars of gold.
365-
366-
If two miners can carry one piece of gold, then the payoff of a
367-
coalition $S$ is:
368-
369-
$${
370-
v(S) = \left\{\begin{array}{lll}
371-
\mid S \mid / 2 & \text{, if} & \mid S \mid \text{ is even} \\
372-
( \mid S \mid - 1)/2 & \text{, if} & \mid S \mid \text{ is odd}
373-
\end{array}\right.
374-
}$$
375-
376-
If there are more than two miners and there is an even number of miners,
377-
then the core consists of the single payoff where each miner gets 1/2.
378-
379-
If there is an odd number of miners, then the core is empty.
380-
381-
Taken from [Wikipedia](https://en.wikipedia.org/wiki/Core_(game_theory))
382-
383-
Args:
384-
n_miners: Number of miners that participate in the game.
385-
"""
386-
387-
def __init__(self, n_miners: int, **kwargs):
388-
if n_miners <= 2:
389-
raise ValueError(f"n_miners, {n_miners} should be > 2")
390-
self.n_miners = n_miners
391-
392-
x = np.arange(n_miners)[..., np.newaxis]
393-
# The y values don't matter here
394-
y = np.zeros_like(x)
395-
396-
self.data = Dataset(x_train=x, y_train=y, x_test=x, y_test=y)
397-
398-
def __call__(self, indices: Iterable[int]) -> float:
399-
n = len(tuple(indices))
400-
if n % 2 == 0:
401-
return n / 2
402-
else:
403-
return (n - 1) / 2
404-
405-
def _initialize_utility_wrapper(self):
406-
pass
407-
408-
def exact_least_core_values(self) -> Tuple[NDArray[np.float_], float]:
409-
if self.n_miners % 2 == 0:
410-
values = np.array([0.5] * self.n_miners)
411-
subsidy = 0.0
412-
else:
413-
values = np.array(
414-
[(self.n_miners - 1) / (2 * self.n_miners)] * self.n_miners
415-
)
416-
subsidy = (self.n_miners - 1) / (2 * self.n_miners)
417-
return values, subsidy
418-
419-
def __repr__(self) -> str:
420-
return f"{self.__class__.__name__}(n={self.n_miners})"
421-
422-
423-
class GlovesGameUtility(Utility):
424-
r"""Toy game utility that is used for testing and demonstration purposes.
425-
426-
In this game, some players have a left glove and others a right glove.
427-
Single gloves have a worth of zero while pairs have a worth of 1.
428-
429-
The payoff of a coalition $S$ is:
430-
431-
$${
432-
v(S) = \min( \mid S \cap L \mid, \mid S \cap R \mid )
433-
}$$
434-
435-
Where $L$, respectively $R$, is the set of players with left gloves,
436-
respectively right gloves.
437-
438-
Args:
439-
left: Number of players with a left glove.
440-
right: Number of player with a right glove.
441-
442-
"""
443-
444-
def __init__(self, left: int, right: int, **kwargs):
445-
self.left = left
446-
self.right = right
447-
448-
x = np.empty(left + right)[..., np.newaxis]
449-
# The y values don't matter here
450-
y = np.zeros_like(x)
451-
452-
self.data = Dataset(x_train=x, y_train=y, x_test=x, y_test=y)
453-
454-
def __call__(self, indices: Iterable[int]) -> float:
455-
left_sum = float(np.sum(np.asarray(indices) < self.left))
456-
right_sum = float(np.sum(np.asarray(indices) >= self.left))
457-
return min(left_sum, right_sum)
458-
459-
def _initialize_utility_wrapper(self):
460-
pass
461-
462-
def exact_least_core_values(self) -> Tuple[NDArray[np.float_], float]:
463-
if self.left == self.right:
464-
subsidy = -0.5
465-
values = np.array([0.5] * (self.left + self.right))
466-
elif self.left < self.right:
467-
subsidy = 0.0
468-
values = np.array([1.0] * self.left + [0.0] * self.right)
469-
else:
470-
subsidy = 0.0
471-
values = np.array([0.0] * self.left + [1.0] * self.right)
472-
return values, subsidy
473-
474-
def __repr__(self) -> str:
475-
return f"{self.__class__.__name__}(L={self.left}, R={self.right})"

0 commit comments

Comments
 (0)