Coverage for mlos_bench/mlos_bench/tests/tunables/tunable_slice_references_test.py: 96%
57 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 00:44 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 00:44 +0000
1#
2# Copyright (c) Microsoft Corporation.
3# Licensed under the MIT License.
4#
5"""Unit tests for unique references to tunables when they're loaded multiple times."""
7import json5 as json
8import pytest
10from mlos_bench.tunables.tunable_groups import TunableGroups
13def test_duplicate_merging_tunable_groups(tunable_groups_config: dict) -> None:
14 """Check that the merging logic of tunable groups works as expected."""
15 parent_tunables = TunableGroups(tunable_groups_config)
17 # Pretend we loaded this one from disk another time.
18 tunables_dup = TunableGroups(tunable_groups_config)
20 (tunable, covariant_group) = next(iter(parent_tunables))
21 (tunable_dup, covariant_group_dup) = next(iter(tunables_dup))
23 assert tunable == tunable_dup
24 assert covariant_group == covariant_group_dup
26 # Test merging prior to making any changes.
27 parent_tunable_copy = parent_tunables.copy()
28 parent_tunables = parent_tunables.merge(tunables_dup)
30 # Check that they're the same.
31 assert covariant_group == covariant_group_dup
32 assert parent_tunables == tunables_dup
33 assert parent_tunables == parent_tunable_copy
35 (tunable_retry, covariant_group_retry) = next(iter(parent_tunables))
36 assert tunable == tunable_retry
37 assert covariant_group == covariant_group_retry
39 # Update a value to indicate that they're separate copies.
40 if tunable.is_categorical:
41 tunable.category = [x for x in tunable.categories if x != tunable.category][0]
42 elif tunable.is_numerical:
43 tunable.numerical_value += 1
45 # Check that they're separate.
46 assert tunable != tunable_dup
47 assert covariant_group != covariant_group_dup
48 assert parent_tunables != tunables_dup
50 # Should be ok since we only changed the value.
51 parent_tunable_copy = parent_tunables.copy()
52 parent_tunables = parent_tunables.merge(tunables_dup)
54 # Make sure nothing changed in the parent.
55 assert tunable != tunable_dup
56 assert covariant_group != covariant_group_dup
57 assert parent_tunables != tunables_dup
58 assert parent_tunables == parent_tunable_copy
61def test_overlapping_group_merge_tunable_groups(tunable_groups_config: dict) -> None:
62 """Check that the merging logic of tunable groups works as expected."""
63 parent_tunables = TunableGroups(tunable_groups_config)
65 # This config should overlap with the parent config.
66 # (same group name, different param name, different values)
67 other_tunables_json = """
68 {
69 "boot": {
70 "cost": 300,
71 "params": {
72 "noidle": {
73 "description": "(different) idling method",
74 "type": "categorical",
75 "default": "nomwait",
76 "values": ["nohalt", "nomwait", "idle"]
77 }
78 }
79 }
80 }
81 """
83 other_tunables_config = json.loads(other_tunables_json)
84 other_tunables = TunableGroups(other_tunables_config)
86 with pytest.raises(ValueError):
87 parent_tunables.merge(other_tunables)
90def test_bad_extended_merge_tunable_group(tunable_groups_config: dict) -> None:
91 """Check that the merging logic of tunable groups works as expected."""
92 parent_tunables = TunableGroups(tunable_groups_config)
94 # This config should overlap with the parent config.
95 # (different group name, same param name)
96 other_tunables_json = """
97 {
98 "new-group": {
99 "cost": 300,
100 "params": {
101 "idle": {
102 "type": "categorical",
103 "description": "Idling method",
104 "default": "mwait",
105 "values": ["halt", "mwait", "noidle"]
106 }
107 }
108 }
109 }
110 """
112 other_tunables_config = json.loads(other_tunables_json)
113 other_tunables = TunableGroups(other_tunables_config)
115 with pytest.raises(ValueError):
116 parent_tunables.merge(other_tunables)
119def test_good_extended_merge_tunable_group(tunable_groups_config: dict) -> None:
120 """Check that the merging logic of tunable groups works as expected."""
121 parent_tunables = TunableGroups(tunable_groups_config)
123 # This config should overlap with the parent config.
124 # (different group name, same param name)
125 other_tunables_json = """
126 {
127 "new-group": {
128 "cost": 300,
129 "params": {
130 "new-param": {
131 "type": "int",
132 "default": 0,
133 "range": [0, 10]
134 }
135 }
136 }
137 }
138 """
140 other_tunables_config = json.loads(other_tunables_json)
141 other_tunables = TunableGroups(other_tunables_config)
143 assert "new-param" not in parent_tunables
144 assert "new-param" in other_tunables
146 parent_tunables = parent_tunables.merge(other_tunables)
148 assert "new-param" in parent_tunables
149 (tunable_param, covariant_group) = parent_tunables.get_tunable("new-param")
150 assert tunable_param.name == "new-param"
151 assert covariant_group.name == "new-group"