|
13 | 13 | Our :func:`select_consistent` tries to find such a consistent dataset using |
14 | 14 | different :class:`Selector` methods that are passed to it as second parameter. |
15 | 15 |
|
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 |
17 | 17 | simply retains the exactly same number of runs for all |
18 | 18 | instance/algorithm/objective/encoding combinations, making sure that the same |
19 | 19 | seeds are used for a given instance. |
@@ -700,6 +700,60 @@ def __str__(self) -> str: |
700 | 700 | return "sameNumberOfRuns" |
701 | 701 |
|
702 | 702 |
|
| 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 | + |
703 | 757 | def __seed_hash(x: tuple[str, int]) -> tuple[str, int]: |
704 | 758 | """ |
705 | 759 | Compute a hash for instance-random seeds. |
@@ -739,6 +793,17 @@ def __seed_hash(x: tuple[str, int]) -> tuple[str, int]: |
739 | 793 | SELECTOR_SAME_RUNS_FOR_ALL: Final[tuple[Selector, ...]] = ( |
740 | 794 | __SameNumberOfRuns(), ) |
741 | 795 |
|
| 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 | + |
742 | 807 | #: A simple selector set for maximum runs. |
743 | 808 | #: This selector first attempts to do the selection given in |
744 | 809 | #: :const:`SELECTOR_SAME_RUNS_FOR_ALL` and then attempts to find |
@@ -865,6 +930,22 @@ def select_consistent( |
865 | 930 | ... a1i1o1e1s1, a2i1o1e1s2, a2i1o1e1s3)))) |
866 | 931 | ['a2/i1/o1/e1/2', 'a2/i1/o1/e1/3'] |
867 | 932 |
|
| 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 | +
|
868 | 949 | >>> try: |
869 | 950 | ... select_consistent((a1i1o1e1s1, a1i1o1e1s2, a1i2o1e1s2, a1i2o1e1s2)) |
870 | 951 | ... except ValueError as ve: |
|
0 commit comments