import re from typing import List, Dict, Any, Callable class ParameterConstraintEngine: def __init__(self): self.constraints = [] self.condition_functions = [] def add_constraint(self, name: str, condition: str, error_message: str = ''): self.constraints.append({ 'name': name, 'condition': condition, 'error_message': error_message or 'Constraint violated: ' + name }) def add_condition_from_config(self, param_name: str, condition_str: str): if not condition_str: return condition_str = condition_str.strip() self.constraints.append({ 'name': param_name, 'condition': condition_str, 'error_message': 'Parameter constraint violated: ' + param_name }) def _parse_condition(self, condition: str) -> Callable[[Dict], bool]: condition = condition.strip() patterns = [ (r'^(\w+)\s*>\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) > p.get(m.group(2), 0)), (r'^(\w+)\s*<\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) < p.get(m.group(2), 0)), (r'^(\w+)\s*>=\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) >= p.get(m.group(2), 0)), (r'^(\w+)\s*<=\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) <= p.get(m.group(2), 0)), (r'^(\w+)\s*==\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) == p.get(m.group(2), 0)), (r'^(\w+)\s*!=\s*(\w+)$', lambda m, p: p.get(m.group(1), 0) != p.get(m.group(2), 0)), (r'^(\w+)\s*>\s*(\d+)$', lambda m, p: p.get(m.group(1), 0) > int(m.group(2))), (r'^(\w+)\s*<\s*(\d+)$', lambda m, p: p.get(m.group(1), 0) < int(m.group(2))), ] for pattern, func in patterns: match = re.match(pattern, condition) if match: return lambda params, m=match, f=func: f(m, params) return lambda params: True def _is_valid_combination(self, params: Dict) -> bool: for constraint in self.constraints: condition = constraint['condition'] if not condition: continue parse_func = self._parse_condition(condition) if not parse_func(params): return False return True def filter_valid_combinations(self, combinations: List[Dict]) -> List[Dict]: valid = [] invalid_count = 0 for combo in combinations: if self._is_valid_combination(combo): valid.append(combo) else: invalid_count += 1 if invalid_count > 0: print('Filtered ' + str(invalid_count) + ' invalid combinations') return valid def get_constraint_count(self) -> int: return len(self.constraints) def create_constraint_engine(ea_config) -> ParameterConstraintEngine: engine = ParameterConstraintEngine() for param_name, param in ea_config.parameters.items(): if param.condition: engine.add_condition_from_config(param_name, param.condition) return engine