Coverage for o2/models/constraints/size_rule_constraints.py: 86%
37 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-05-16 11:18 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-05-16 11:18 +0000
1from dataclasses import dataclass
2from typing import TYPE_CHECKING, Callable, Optional, TypeGuard
4from dataclass_wizard import JSONWizard
5from sympy import Symbol, lambdify
7from o2.models.constraints.batching_constraints import BatchingConstraints
8from o2.models.timetable import RULE_TYPE, FiringRule
10if TYPE_CHECKING:
11 from o2.models.timetable import TimetableType
14@dataclass(frozen=True)
15class SizeRuleConstraints(BatchingConstraints, JSONWizard):
16 """Size rule constraints for batching."""
18 duration_fn: str
19 cost_fn: str
20 min_size: Optional[int]
21 max_size: Optional[int]
23 class _(JSONWizard.Meta): # noqa: N801
24 key_transform_with_dump = "SNAKE"
25 tag = RULE_TYPE.SIZE.value
26 tag_key = "rule_type"
28 def verify_timetable(self, timetable: "TimetableType") -> bool:
29 """Check if the timetable is valid against the constraints."""
30 rules: list[FiringRule[int]] = timetable.get_firing_rules_for_tasks(
31 self.tasks, rule_type=RULE_TYPE.SIZE, batch_type=self.batch_type
32 )
33 return all(self._verify_firing_rule(rule) for rule in rules)
35 def _verify_firing_rule(self, firing_rule: FiringRule[int]) -> bool:
36 """Check if the firing rule is valid against the constraints."""
37 # Special case: Size 0 is always invalid
38 if firing_rule.value == 0:
39 from o2.util.logger import warn
41 warn("Warning: Size 0 rule was created!")
42 return False
44 # Special case: If the rule is a <(=) min size, it is invalid if min size is set
45 if self.min_size and self.min_size > 0 and (firing_rule.is_lt_or_lte):
46 return False
48 # Special case: If the rule is a >(=) max size, it is invalid if max size is set
49 # TODO: Think about this
50 # if self.max_size and (firing_rule.is_gt_or_gte):
51 # return False
53 if (self.min_size and firing_rule.value < self.min_size) or ( # noqa: SIM103
54 self.max_size and firing_rule.value > self.max_size
55 ):
56 return False
58 return True
60 @property
61 def cost_fn_lambda(self) -> Callable[[float], float]:
62 """Get the cost function as a lambda function."""
63 return lambdify(Symbol("size"), self.cost_fn)
65 @property
66 def duration_fn_lambda(self) -> Callable[[float], float]:
67 """Get the duration function as a lambda function."""
68 return lambdify(Symbol("size"), self.duration_fn)
71def is_size_constraint(val: BatchingConstraints) -> TypeGuard[SizeRuleConstraints]:
72 """Check if the constraint is a size constraint."""
73 return val.rule_type == RULE_TYPE.SIZE