Coverage for moptipy / api / objective.py: 100%
26 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
1"""
2The base class for implementing objective functions.
4An objective function evaluates the quality of a candidate solution of an
5optimization problem. Solutions with smaller objective values are better,
6i.e., objective functions are subject to minimization. All objective
7functions inherit from :class:`~moptipy.api.objective.Objective`. If you
8implement a new objective function, you can test it via the pre-defined unit
9test routine :func:`~moptipy.tests.objective.validate_objective`.
10"""
11from math import inf, isfinite
12from typing import Any
14from pycommons.types import type_error
16from moptipy.api import logging
17from moptipy.api.component import Component
18from moptipy.utils.logger import KeyValueLogSection
21# start book
22class Objective(Component):
23 """
24 An objective function subject to minimization.
26 An objective function represents one optimization criterion that
27 is used for rating the solution quality. All objective functions in
28 our system are subject to minimization, meaning that smaller values
29 are better.
30 """
32 def evaluate(self, x) -> float | int:
33 """
34 Evaluate a solution `x` and return its objective value.
36 The return value is either an integer or a float and must be
37 finite. Smaller objective values are better, i.e., all objective
38 functions are subject to minimization.
40 :param x: the candidate solution
41 :return: the objective value
42 """
43 # end book
45 def lower_bound(self) -> float | int:
46 """
47 Get the lower bound of the objective value.
49 This function returns a theoretical limit for how good a solution
50 could be at best. If no real limit is known, the function should
51 return `-inf`.
53 :return: the lower bound of the objective value
54 """
55 return -inf
57 def upper_bound(self) -> float | int:
58 """
59 Get the upper bound of the objective value.
61 This function returns a theoretical limit for how bad a solution could
62 be at worst. If no real limit is known, the function should return
63 `inf`.
65 :return: the upper bound of the objective value
66 """
67 return inf
69 def is_always_integer(self) -> bool:
70 """
71 Return `True` if :meth:`~evaluate` will always return an `int` value.
73 :returns: `True` if :meth:`~evaluate` will always return an `int`
74 or `False` if also a `float` may be returned.
75 """
76 return False
78 def log_parameters_to(self, logger: KeyValueLogSection) -> None:
79 """
80 Log the parameters of this function to the provided destination.
82 :param logger: the logger for the parameters
83 """
84 super().log_parameters_to(logger)
85 b = self.lower_bound()
86 if isinstance(b, int) or isfinite(b):
87 logger.key_value(logging.KEY_F_LOWER_BOUND, b)
88 b = self.upper_bound()
89 if isinstance(b, int) or isfinite(b):
90 logger.key_value(logging.KEY_F_UPPER_BOUND, b)
93def check_objective(objective: Any) -> Objective:
94 """
95 Check whether an object is a valid instance of :class:`Objective`.
97 :param objective: the objective
98 :return: the objective
99 :raises TypeError: if `objective` is not an instance of
100 :class:`Objective`
102 >>> check_objective(Objective())
103 Objective
104 >>> try:
105 ... check_objective('A')
106 ... except TypeError as te:
107 ... print(te)
108 objective function should be an instance of moptipy.api.objective.\
109Objective but is str, namely 'A'.
110 >>> try:
111 ... check_objective(None)
112 ... except TypeError as te:
113 ... print(te)
114 objective function should be an instance of moptipy.api.objective.\
115Objective but is None.
116 """
117 if isinstance(objective, Objective):
118 return objective
119 raise type_error(objective, "objective function", Objective)