Coverage for sparkle/types/__init__.py: 88%
41 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-07 15:22 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-07 15:22 +0000
1"""This package provides types for Sparkle applications."""
2import importlib
3import inspect
4import re
5from typing import Callable
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
14objective_string_regex = re.compile(
15 r"(?P<name>[\w\-_]+)(:(?P<direction>min|max))?(:(?P<type>metric|objective))?$")
16objective_variable_regex = re.compile(r"(-?\d+)$")
19def _check_class(candidate: Callable) -> bool:
20 """Verify whether a loaded class is a valid objective class."""
21 return inspect.isclass(candidate) and issubclass(candidate, SparkleObjective)
24def resolve_objective(objective_name: str) -> SparkleObjective:
25 """Try to resolve the objective class by (case-sensitive) name.
27 convention: objective_name(variable-k)?(:[min|max])?(:[metric|objective])?
28 Here, min|max refers to the minimisation or maximisation of the objective
29 and metric|objective refers to whether the objective should be optimized
30 or just recorded.
32 Order of resolving:
33 class_name of user defined SparkleObjectives
34 class_name of sparkle defined SparkleObjectives
35 default SparkleObjective with minimization unless specified as max
37 Args:
38 name: The name of the objective class. Can include parameter value k.
40 Returns:
41 Instance of the Objective class or None if not found.
42 """
43 match = objective_string_regex.fullmatch(objective_name)
44 if match is None or objective_name == "" or not objective_name[0].isalpha():
45 return None
47 name = match.group("name")
48 minimise = not match.group("direction") == "max" # .group returns "" if no match
49 metric = match.group("type") == "metric"
51 # Search for optional variable and record split point between name and variable
52 name_options = [(name, None), ] # Options of names to check for
53 if m := objective_variable_regex.search(name):
54 argument = int(m.group())
55 name_options = [(name[:m.start()], argument), ] + name_options # Prepend
57 # First try to resolve the user input classes
58 for rname, rarg in name_options:
59 try:
60 user_module = importlib.import_module("Settings.objective")
61 for o_name, o_class in inspect.getmembers(user_module,
62 predicate=_check_class):
63 if o_name == rname:
64 if rarg is not None:
65 return o_class(rarg, minimise=minimise, metric=metric)
66 return o_class(minimise=minimise, metric=metric)
67 except Exception:
68 pass
70 for rname, rarg in name_options:
71 # Try to match with specially defined classes
72 for o_name, o_class in inspect.getmembers(objective,
73 predicate=_check_class):
74 if o_name == rname:
75 if rarg is not None:
76 return o_class(rarg, minimise=minimise, metric=metric)
77 return o_class(minimise=minimise, metric=metric)
79 # No special objects found. Return objective with full name
80 return SparkleObjective(name=objective_name, minimise=minimise, metric=metric)