import math import abc from .SlidingWindow import SlidingWindow class Cost(abc.ABC): def __init__(self, temp_min=19.5, temp_max=20.6, params=None): if params is None: params = ('heating', 'heater_on', 'heater_off', 'discomfort') self.params = params self.costs = {} for param in params: self.costs[param] = 0.0 self.temp_min = temp_min self.temp_max = temp_max def total(self): return sum(self.costs.values()) def __lt__(self, other): return self.total() < other.total() def __str__(self): result = '' for param in self.params: result += '{}={} '.format(param, self.costs[param]) result += 'total={}'.format(self.total()) return result @abc.abstractmethod def compute_partial_cost(self, window: SlidingWindow) -> dict: pass def add_partial_cost(self, window: SlidingWindow) -> dict: partial_cost = self.compute_partial_cost(window) for k, v in partial_cost.items(): self.costs[k] += v return partial_cost class DeviationCost(Cost): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs, params=('step', 'diff', 'diff_sum', 'mean', 'sq_err')) def compute_partial_cost(self, window: SlidingWindow) -> dict: partial_cost = {} temp = window.get_current_value('temp_in') temp_calc = window.get_current_orig_value('temp_in') diff = abs(temp - temp_calc) partial_cost['diff_sum'] = diff partial_cost['mean'] = diff * diff partial_cost['step'] = 1 return partial_cost def add_partial_cost(self, window: SlidingWindow) -> dict: result = super().add_partial_cost(window) self.costs['sq_err'] = self.costs['mean'] / self.costs['step'] self.costs['diff'] = max(self.costs['diff'], result['diff_sum']) return result class NegativeCostBase(Cost): def compute_partial_cost(self, window: SlidingWindow): partial_cost = {} partial_cost['heating'] = window.get_previous_value('mode') temperature = window.get_next_value('temp_in') deviation = 0 if temperature < self.temp_min: deviation = self.temp_min - temperature elif temperature > self.temp_max: deviation = temperature - self.temp_max if deviation: partial_cost['discomfort'] = pow(deviation * 11, 2) return partial_cost class NegativeCost(Cost): def compute_partial_cost(self, window: SlidingWindow): mode = window.get_previous_value('mode') last_mode = window.get_value('mode', -3) temperature = window.get_current_value('temp_in') partial_cost = {} if mode: partial_cost['heating'] = 1 if last_mode is not None: if not last_mode and mode: partial_cost['heater_on'] = 3 elif last_mode and not mode: partial_cost['heater_off'] = 4 deviation = 0 if temperature < self.temp_min: deviation = self.temp_min - temperature elif temperature > self.temp_max: deviation = temperature - self.temp_max if deviation: partial_cost['discomfort'] = pow(deviation * 11, 2) return partial_cost class NegativeCostExtendedRange(NegativeCost): def __init__(self): super().__init__(temp_max=20.6)