Coverage for sparkle/types/__init__.py: 88%

40 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-27 09:10 +0000

1"""This package provides types for Sparkle applications.""" 

2import importlib 

3import inspect 

4import re 

5from typing import Callable 

6 

7from sparkle.types.sparkle_callable import SparkleCallable 

8from sparkle.types.features import FeatureGroup, FeatureSubgroup, FeatureType 

9from sparkle.types.status import SolverStatus 

10from sparkle.types import objective 

11from sparkle.types.objective import SparkleObjective, UseTime 

12 

13 

14objective_string_regex = re.compile(r"(?P<name>[\w\-_]+)(:(?P<direction>min|max))?$") 

15objective_variable_regex = re.compile(r"(-?\d+)$") 

16 

17 

18def _check_class(candidate: Callable) -> bool: 

19 """Verify whether a loaded class is a valid objective class.""" 

20 return inspect.isclass(candidate) and issubclass(candidate, SparkleObjective) 

21 

22 

23def resolve_objective(objective_name: str) -> SparkleObjective: 

24 """Try to resolve the objective class by (case-sensitive) name. 

25 

26 convention: objective_name(variable-k)?(:[min|max])? 

27 

28 Order of resolving: 

29 class_name of user defined SparkleObjectives 

30 class_name of sparkle defined SparkleObjectives 

31 default SparkleObjective with minimization unless specified as max 

32 

33 Args: 

34 name: The name of the objective class. Can include parameter value k. 

35 

36 Returns: 

37 Instance of the Objective class or None if not found. 

38 """ 

39 match = objective_string_regex.fullmatch(objective_name) 

40 if match is None or objective_name == "" or not objective_name[0].isalpha(): 

41 return None 

42 

43 name = match.group("name") 

44 minimise = not match.group("direction") == "max" # .group returns "" if no match 

45 

46 # Search for optional variable and record split point between name and variable 

47 name_options = [(name, None), ] # Options of names to check for 

48 if m := objective_variable_regex.search(name): 

49 argument = int(m.group()) 

50 name_options = [(name[:m.start()], argument), ] + name_options # Prepend 

51 

52 # First try to resolve the user input classes 

53 for rname, rarg in name_options: 

54 try: 

55 user_module = importlib.import_module("Settings.objective") 

56 for o_name, o_class in inspect.getmembers(user_module, 

57 predicate=_check_class): 

58 if o_name == rname: 

59 if rarg is not None: 

60 return o_class(rarg) 

61 return o_class() 

62 except Exception: 

63 pass 

64 

65 for rname, rarg in name_options: 

66 # Try to match with specially defined classes 

67 for o_name, o_class in inspect.getmembers(objective, 

68 predicate=_check_class): 

69 if o_name == rname: 

70 if rarg is not None: 

71 return o_class(rarg) 

72 return o_class() 

73 

74 # No special objects found. Return objective with full name 

75 return SparkleObjective(name=objective_name, minimise=minimise)