1 # Copyright (c) 2023 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Module defining Config class."""
16 from collections.abc import Iterable
17 from dataclasses import dataclass
18 from typing import Optional
20 from .dataclass import DataclassProperty
21 from .search_goal import SearchGoal
22 from .search_goal_tuple import SearchGoalTuple
27 """Structure containing several static config items.
29 The main MLRsearch algorithm uses multiple customizable values.
30 Pylint complains if the values appear as long argument lists
31 or multiple local variables.
33 This class offers a storage for values which do not contain
34 internally mutable state and are set at an unknown time
35 before the search starts. This way users can override only some values,
36 and do it over multiple calls.
37 All "official" user inputs are contained here.
39 Properties are defined to enforce the requirements on allowed values.
40 All fields have default values, so instances can be created without any.
41 It is still recommended to set all values after instantiation,
42 as the defaults may change in the next version.
44 As some relations between values of different fields are required,
45 users must take care to set them in the correct order.
47 For example, min_load has to be set to a value smaller
48 than the current value of max_load.
51 # Externally visible "fields" (but in fact redefined as properties).
52 goals: SearchGoalTuple = SearchGoalTuple((SearchGoal(),))
53 """Container holding search goals."""
54 min_load: float = 9001.0
55 """Each trial measurement must have intended load at least this [tps]."""
57 """Each trial measurement must have intended load at most this [tps]."""
58 search_duration_max: float = 1200.0
59 """The search will end as a failure this long [s] after it is started."""
60 warmup_duration: float = 1.0
61 """If specified, one trial at max load and this duration is performed
62 before the usual search starts. None converts to zero and means no warmup.
63 The results of that one trial are ignored."""
66 def goals(self) -> SearchGoalTuple:
67 """Return the reference to the current container of goals.
69 :returns: The current container instance.
70 :rtype: SearchGoalTuple
75 def goals(self, goals: Iterable[SearchGoal]) -> None:
76 """Create and store the goal container.
78 :param goals: Search goals to add to the container to store.
79 :type goals: Iterable[SearchGoal]
80 :raises ValueError: If there are no goals.
81 :raises TypeError: If any of the goals is not a SearchGoal.
83 self._goals = SearchGoalTuple(goals)
86 def min_load(self) -> float:
87 """Getter for min load, no logic here.
89 :returns: Currently set minimal intended load [tps].
95 def min_load(self, load: float) -> None:
96 """Set min load after converting type and checking value.
98 :param load: Minimal intended load [tps] to set.
100 :raises ValueError: If the argument is found invalid.
104 raise ValueError(f"Min load {load} must be positive.")
105 # At the time init is first called, _max_load is not set yet.
106 if hasattr(self, "_max_load") and load >= self.max_load:
107 raise ValueError(f"Min load {load} must be smaller.")
108 self._min_load = load
111 def max_load(self) -> float:
112 """Getter for max load, no logic here.
114 :returns: Currently set maximal intended load [tps].
117 return self._max_load
120 def max_load(self, load: float) -> None:
121 """Set max load after converting type and checking value.
123 :param load: Minimal intended load [tps] to set.
125 :raises ValueError: If the argument is found invalid.
128 if load <= self.min_load:
129 raise ValueError(f"Max load {load} must be bigger.")
130 self._max_load = load
133 def search_duration_max(self) -> float:
134 """Getter for max search duration, no logic here.
136 :returns: Currently set max search duration [s].
139 return self._search_duration_max
141 @search_duration_max.setter
142 def search_duration_max(self, duration: float) -> None:
143 """Set max search duration after converting and checking value.
145 :param duration: Search duration maximum [s] to set.
146 :type duration: float
147 :raises ValueError: If the argument is found invalid.
149 duration = float(duration)
151 raise ValueError(f"Search duration max too small: {duration}")
152 self._search_duration_max = duration
155 def warmup_duration(self) -> float:
156 """Getter for warmup duration, no logic here.
158 :returns: Currently set max search duration [s].
161 return self._warmup_duration
163 @warmup_duration.setter
164 def warmup_duration(self, duration: Optional[float]) -> None:
165 """Set warmup duration after converting and checking value.
167 Zero duration is treated as None, meaning no warmup trial.
169 :param duration: Warmup duration [s] to set.
170 :type duration: Optional(float)
171 :raises ValueError: If the argument is found invalid.
174 duration = float(duration)
176 raise ValueError(f"Warmup duration too small: {duration}")
179 self._warmup_duration = duration