Skip to content

Commit 1851f78

Browse files
committed
added SELECTOR_SAME_SEEDS_FOR_ALL selector
1 parent 1eafc8c commit 1851f78

File tree

5 files changed

+86
-5
lines changed

5 files changed

+86
-5
lines changed

moptipy/evaluation/selector.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
Our :func:`select_consistent` tries to find such a consistent dataset using
1414
different :class:`Selector` methods that are passed to it as second parameter.
1515
16-
In the most basic and defaul setting, :const:`SELECTOR_SAME_RUNS_FOR_ALL`, it
16+
In the most basic and default setting, :const:`SELECTOR_SAME_RUNS_FOR_ALL`, it
1717
simply retains the exactly same number of runs for all
1818
instance/algorithm/objective/encoding combinations, making sure that the same
1919
seeds are used for a given instance.
@@ -700,6 +700,60 @@ def __str__(self) -> str:
700700
return "sameNumberOfRuns"
701701

702702

703+
class __SameSeedsForAllAlgorithmsPerInstance(Selector):
704+
"""A selector choosing the same runs for all algorithms."""
705+
706+
def select(self,
707+
dims: tuple[int, int, int, int, int],
708+
keys: tuple[tuple[int, int, int, int, int], ...],
709+
best_length: int) -> list[tuple[
710+
int, int, int, int, int]] | None:
711+
"""
712+
Perform the dataset selection.
713+
714+
:param dims: the number of different values per key dimension
715+
:param keys: the data keys to select from
716+
:param best_length: the length of the best-so-far set of runs,
717+
will be `-1` if no such best exists
718+
:return: the selected data
719+
"""
720+
algos: set[tuple[int, int, int]] = {
721+
(key[KEY_ALGORITHM], key[KEY_ENCODING], key[KEY_OBJECTIVE])
722+
for key in keys}
723+
inst_algo_seeds: list[dict[tuple[int, int, int], set[int]]] = [
724+
{algo: set() for algo in algos} for _ in range(
725+
dims[KEY_INSTANCE])]
726+
n_combos: int = 0
727+
for key in keys:
728+
n_combos += 1
729+
inst_algo_seeds[key[KEY_INSTANCE]][
730+
key[KEY_ALGORITHM], key[KEY_ENCODING],
731+
key[KEY_OBJECTIVE]].add(key[KEY_SEED])
732+
733+
if n_combos <= 0:
734+
raise ValueError("No runs??")
735+
736+
result: list[tuple[int, int, int, int, int]] = []
737+
for inst in inst_algo_seeds:
738+
algo_seeds = next(iter(inst.values()))
739+
for seeds in inst.values():
740+
algo_seeds.intersection_update(seeds)
741+
result.extend(key for key in keys if key[KEY_SEED] in algo_seeds)
742+
743+
if len(result) == 0:
744+
return None
745+
logger(f"Got {len(result)} seeds to use.")
746+
return result
747+
748+
def __str__(self) -> str:
749+
"""
750+
Get the text representation of this selector.
751+
752+
:returns "sameSeedsPerInstance": always
753+
"""
754+
return "sameSeedsPerInstance"
755+
756+
703757
def __seed_hash(x: tuple[str, int]) -> tuple[str, int]:
704758
"""
705759
Compute a hash for instance-random seeds.
@@ -739,6 +793,17 @@ def __seed_hash(x: tuple[str, int]) -> tuple[str, int]:
739793
SELECTOR_SAME_RUNS_FOR_ALL: Final[tuple[Selector, ...]] = (
740794
__SameNumberOfRuns(), )
741795

796+
#: This selector is different from the others: It may create combinations
797+
#: where there are more runs on some instances than on others.
798+
#: It is a selector that chooses the same seeds per instance.
799+
#: It may select an uneven number of runs, meaning that on some instances,
800+
#: there may be more runs than on others.
801+
#: Some instances may be dropped altogether.
802+
#: However, on all instances that are not dropped, all algorithm/
803+
#: encoding/objective combinations have performed the same runs.
804+
SELECTOR_SAME_SEEDS_FOR_ALL: Final[tuple[Selector, ...]] = (
805+
__SameSeedsForAllAlgorithmsPerInstance(), )
806+
742807
#: A simple selector set for maximum runs.
743808
#: This selector first attempts to do the selection given in
744809
#: :const:`SELECTOR_SAME_RUNS_FOR_ALL` and then attempts to find
@@ -865,6 +930,22 @@ def select_consistent(
865930
... a1i1o1e1s1, a2i1o1e1s2, a2i1o1e1s3))))
866931
['a2/i1/o1/e1/2', 'a2/i1/o1/e1/3']
867932
933+
>>> list(map(__p, select_consistent((
934+
... a1i1o1e1s1, a1i1o1e1s2, a1i1o1e1s3,
935+
... a1i2o1e1s1, a1i2o1e1s2, a1i2o1e1s3,
936+
... a2i1o1e1s1, a2i1o1e1s2,
937+
... a2i2o1e1s2, a2i2o1e1s3), SELECTOR_SAME_SEEDS_FOR_ALL)))
938+
['a1/i1/o1/e1/1', 'a1/i1/o1/e1/2', 'a1/i2/o1/e1/2', 'a1/i2/o1/e1/3', \
939+
'a2/i1/o1/e1/1', 'a2/i1/o1/e1/2', 'a2/i2/o1/e1/2', 'a2/i2/o1/e1/3']
940+
941+
>>> list(map(__p, select_consistent((
942+
... a1i1o1e1s1, a1i1o1e1s2, a1i1o1e1s3,
943+
... a1i2o1e1s1, a1i2o1e1s2, a1i2o1e1s3,
944+
... a2i1o1e1s1, a2i1o1e1s2,
945+
... a2i2o1e1s2, ), SELECTOR_SAME_SEEDS_FOR_ALL)))
946+
['a1/i1/o1/e1/1', 'a1/i1/o1/e1/2', 'a1/i2/o1/e1/2', \
947+
'a2/i1/o1/e1/1', 'a2/i1/o1/e1/2', 'a2/i2/o1/e1/2']
948+
868949
>>> try:
869950
... select_consistent((a1i1o1e1s1, a1i1o1e1s2, a1i2o1e1s2, a1i2o1e1s2))
870951
... except ValueError as ve:

moptipy/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
from typing import Final
33

44
#: the version string of `moptipy`
5-
__version__: Final[str] = "0.9.172"
5+
__version__: Final[str] = "0.9.173"

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
#
99

1010
# We use the unified build process offered by pycommons.
11-
pycommons[dev] == 0.8.88
11+
pycommons[dev] == 0.8.89

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ psutil == 7.2.2
3939

4040
# pycommons offers many of the tools and utilities used in moptipy that are
4141
# not related to optimization.
42-
pycommons == 0.8.88
42+
pycommons == 0.8.89
4343

4444
# scipy provides statistical tests *and* numerical optimization methods that
4545
# we wrap into our API.

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ install_requires =
7979
numba >= 0.63.1
8080
matplotlib >= 3.10.8
8181
psutil >= 7.2.2
82-
pycommons >= 0.8.88
82+
pycommons >= 0.8.89
8383
scipy >= 1.17.0
8484
packages = find:
8585
python_requires = >= 3.12

0 commit comments

Comments
 (0)