|
27 | 27 | from ... import timeseries |
28 | 28 | from ...timeseries import Power |
29 | 29 | from ._base_classes import BaseAlgorithm, Proposal, Report |
| 30 | +from ._bounds_methods import ( |
| 31 | + _adjust_exclusion_bounds, |
| 32 | + _check_exclusion_bounds_overlap, |
| 33 | + _clamp_to_bounds, |
| 34 | +) |
30 | 35 | from ._sorted_set import SortedSet |
31 | 36 |
|
32 | 37 | if typing.TYPE_CHECKING: |
|
36 | 41 | _logger = logging.getLogger(__name__) |
37 | 42 |
|
38 | 43 |
|
39 | | -# Just 20 lines of code in this function, but unfortunately 8 of those are return |
40 | | -# statements, and that's too many for pylint. |
41 | | -def _clamp_to_bounds( # pylint: disable=too-many-return-statements |
42 | | - value: Power, |
43 | | - lower_bound: Power, |
44 | | - upper_bound: Power, |
45 | | - exclusion_bounds: timeseries.Bounds[Power] | None, |
46 | | -) -> tuple[Power | None, Power | None]: |
47 | | - """Clamp the given value to the given bounds. |
48 | | -
|
49 | | - When the given value can falls within the exclusion zone, and can be clamped to |
50 | | - both sides, both options will be returned. |
51 | | -
|
52 | | - When the given value falls outside the usable bounds and can be clamped only to |
53 | | - one side, only that option will be returned. |
54 | | -
|
55 | | - Args: |
56 | | - value: The value to clamp. |
57 | | - lower_bound: The lower bound to clamp to. |
58 | | - upper_bound: The upper bound to clamp to. |
59 | | - exclusion_bounds: The exclusion bounds to clamp outside of. |
60 | | -
|
61 | | - Returns: |
62 | | - The clamped value. |
63 | | - """ |
64 | | - # If the given bounds are within the exclusion bounds, return zero. |
65 | | - # |
66 | | - # And if the given bounds overlap with the exclusion bounds on one side, and the |
67 | | - # given power is in that overlap region, clamp it to the exclusion bounds on that |
68 | | - # side. |
69 | | - if exclusion_bounds is not None: |
70 | | - match _check_exclusion_bounds_overlap( |
71 | | - lower_bound, upper_bound, exclusion_bounds |
72 | | - ): |
73 | | - case (True, True): |
74 | | - return None, None |
75 | | - case (True, False): |
76 | | - if value < exclusion_bounds.upper: |
77 | | - return None, exclusion_bounds.upper |
78 | | - case (False, True): |
79 | | - if value > exclusion_bounds.lower: |
80 | | - return exclusion_bounds.lower, None |
81 | | - |
82 | | - # If the given value is outside the given bounds, clamp it to the closest bound. |
83 | | - if value < lower_bound: |
84 | | - return lower_bound, None |
85 | | - if value > upper_bound: |
86 | | - return None, upper_bound |
87 | | - |
88 | | - # If the given value is within the exclusion bounds and the exclusion bounds are |
89 | | - # within the given bounds, clamp the given value to the closest exclusion bound. |
90 | | - if exclusion_bounds is not None: |
91 | | - if exclusion_bounds.lower < value < exclusion_bounds.upper: |
92 | | - return exclusion_bounds.lower, exclusion_bounds.upper |
93 | | - |
94 | | - return value, value |
95 | | - |
96 | | - |
97 | | -def _check_exclusion_bounds_overlap( |
98 | | - lower_bound: Power, |
99 | | - upper_bound: Power, |
100 | | - exclusion_bounds: timeseries.Bounds[Power] | None, |
101 | | -) -> tuple[bool, bool]: |
102 | | - """Check if the given bounds overlap with the given exclusion bounds. |
103 | | -
|
104 | | - When only the upper bound overlaps with exclusion bounds, the usable range is |
105 | | - between the lower bound and the lower exclusion bound, like below. |
106 | | -
|
107 | | - ===lb+++++++ex----ub-------ex=== |
108 | | -
|
109 | | - When only the lower bound overlaps with exclusion bounds, the usable range is |
110 | | - between the upper exclusion bound and the upper bound. |
111 | | -
|
112 | | - ===ex------lb------ex++++++ub=== |
113 | | -
|
114 | | - Both bounds overlapping with exclusion bounds (or given bounds are fully contained |
115 | | - within exclusion bounds). In this case, there is no usable range. |
116 | | -
|
117 | | - ===ex------lb------ub------ex=== |
118 | | -
|
119 | | - Args: |
120 | | - lower_bound: The lower bound to check. |
121 | | - upper_bound: The upper bound to check. |
122 | | - exclusion_bounds: The exclusion bounds to check against. |
123 | | -
|
124 | | - Returns: |
125 | | - A tuple containing a boolean indicating if the lower bound is bounded by the |
126 | | - exclusion bounds, and a boolean indicating if the upper bound is bounded by |
127 | | - the exclusion bounds. |
128 | | - """ |
129 | | - if exclusion_bounds is None: |
130 | | - return False, False |
131 | | - |
132 | | - bounded_lower = False |
133 | | - bounded_upper = False |
134 | | - |
135 | | - if exclusion_bounds.lower < lower_bound < exclusion_bounds.upper: |
136 | | - bounded_lower = True |
137 | | - if exclusion_bounds.lower < upper_bound < exclusion_bounds.upper: |
138 | | - bounded_upper = True |
139 | | - |
140 | | - return bounded_lower, bounded_upper |
141 | | - |
142 | | - |
143 | | -def _adjust_exclusion_bounds( |
144 | | - lower_bound: Power, |
145 | | - upper_bound: Power, |
146 | | - exclusion_bounds: timeseries.Bounds[Power] | None, |
147 | | -) -> tuple[Power, Power]: |
148 | | - """Adjust the given bounds to exclude the given exclusion bounds. |
149 | | -
|
150 | | - Args: |
151 | | - lower_bound: The lower bound to adjust. |
152 | | - upper_bound: The upper bound to adjust. |
153 | | - exclusion_bounds: The exclusion bounds to adjust to. |
154 | | -
|
155 | | - Returns: |
156 | | - The adjusted lower and upper bounds. |
157 | | - """ |
158 | | - if exclusion_bounds is None: |
159 | | - return lower_bound, upper_bound |
160 | | - |
161 | | - # If the given bounds are within the exclusion bounds, there's no room to adjust, |
162 | | - # so return zero. |
163 | | - # |
164 | | - # And if the given bounds overlap with the exclusion bounds on one side, then clamp |
165 | | - # the given bounds on that side. |
166 | | - match _check_exclusion_bounds_overlap(lower_bound, upper_bound, exclusion_bounds): |
167 | | - case (True, True): |
168 | | - return Power.zero(), Power.zero() |
169 | | - case (False, True): |
170 | | - return lower_bound, exclusion_bounds.lower |
171 | | - case (True, False): |
172 | | - return exclusion_bounds.upper, upper_bound |
173 | | - return lower_bound, upper_bound |
174 | | - |
175 | | - |
176 | 44 | class Matryoshka(BaseAlgorithm): |
177 | 45 | """The matryoshka algorithm.""" |
178 | 46 |
|
|
0 commit comments