Coverage for mlos_bench/mlos_bench/tests/tunables/tunable_slice_references_test.py: 96%

57 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-10-07 01:52 +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.""" 

6 

7import json5 as json 

8import pytest 

9 

10from mlos_bench.tunables.tunable_groups import TunableGroups 

11 

12 

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) 

16 

17 # Pretend we loaded this one from disk another time. 

18 tunables_dup = TunableGroups(tunable_groups_config) 

19 

20 (tunable, covariant_group) = next(iter(parent_tunables)) 

21 (tunable_dup, covariant_group_dup) = next(iter(tunables_dup)) 

22 

23 assert tunable == tunable_dup 

24 assert covariant_group == covariant_group_dup 

25 

26 # Test merging prior to making any changes. 

27 parent_tunable_copy = parent_tunables.copy() 

28 parent_tunables = parent_tunables.merge(tunables_dup) 

29 

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 

34 

35 (tunable_retry, covariant_group_retry) = next(iter(parent_tunables)) 

36 assert tunable == tunable_retry 

37 assert covariant_group == covariant_group_retry 

38 

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 

44 

45 # Check that they're separate. 

46 assert tunable != tunable_dup 

47 assert covariant_group != covariant_group_dup 

48 assert parent_tunables != tunables_dup 

49 

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) 

53 

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 

59 

60 

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) 

64 

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 """ 

82 

83 other_tunables_config = json.loads(other_tunables_json) 

84 other_tunables = TunableGroups(other_tunables_config) 

85 

86 with pytest.raises(ValueError): 

87 parent_tunables.merge(other_tunables) 

88 

89 

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) 

93 

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 """ 

111 

112 other_tunables_config = json.loads(other_tunables_json) 

113 other_tunables = TunableGroups(other_tunables_config) 

114 

115 with pytest.raises(ValueError): 

116 parent_tunables.merge(other_tunables) 

117 

118 

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) 

122 

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 """ 

139 

140 other_tunables_config = json.loads(other_tunables_json) 

141 other_tunables = TunableGroups(other_tunables_config) 

142 

143 assert "new-param" not in parent_tunables 

144 assert "new-param" in other_tunables 

145 

146 parent_tunables = parent_tunables.merge(other_tunables) 

147 

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"