-
Notifications
You must be signed in to change notification settings - Fork 49
Unified Interface: Absolute Tolerance #1660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 9 commits
816df12
cef62b6
649cb24
e5ab895
1940557
d7e86fc
684077a
cbae8ff
4c7ad9c
c2f4d73
be9fd7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -413,6 +413,58 @@ def set_maxeval(self, evaluations: int) -> None: | |
| f"Check supports_maxeval() before calling set_maxeval()." | ||
| ) | ||
|
|
||
| def supports_f_abs_tol(self) -> bool: | ||
| """ | ||
| Check whether optimizer supports absolute function value tolerance. | ||
|
|
||
| Returns | ||
| ------- | ||
| True if optimizer supports setting an absolute tolerance on the | ||
| objective function value, False otherwise. | ||
| """ | ||
| return True | ||
|
|
||
| def set_f_abs_tol(self, tol: float) -> None: | ||
| """ | ||
| Set the absolute tolerance on function value for optimization. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tol | ||
| Absolute tolerance on objective function value for termination. | ||
|
|
||
| Raises | ||
| ------ | ||
| NotImplementedError | ||
| If the optimizer does not support absolute function tolerance. | ||
| """ | ||
| raise NotImplementedError( | ||
| f"{self.__class__.__name__} does not support absolute function tolerance. " | ||
| f"Check supports_f_abs_tol() before calling set_f_abs_tol()." | ||
| ) | ||
|
|
||
| def _set_option_tol(self, tol: float, option_key: str) -> None: | ||
| """ | ||
| Set tolerance in options dict with validation. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tol | ||
| Absolute tolerance value (must be positive). | ||
| option_key | ||
| The key to use in the options dictionary. | ||
|
|
||
| Raises | ||
| ------ | ||
| ValueError | ||
| If tolerance is not positive. | ||
| """ | ||
| if tol < 0: | ||
| raise ValueError(f"Tolerance must be positive, got {tol}") | ||
| if self.options is None: | ||
| self.options = {} | ||
| self.options[option_key] = tol | ||
|
|
||
|
|
||
| class ScipyOptimizer(Optimizer): | ||
| """ | ||
|
|
@@ -722,6 +774,24 @@ def set_maxtime(self, seconds: float) -> None: | |
|
|
||
| self._maxtime_seconds = seconds | ||
|
|
||
| def set_f_abs_tol(self, tol: float) -> None: | ||
| """ | ||
| Set the absolute tolerance on function value for optimization. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tol | ||
| Absolute tolerance on objective function value for termination. | ||
|
|
||
| Raises | ||
| ------ | ||
| ValueError | ||
| If tolerance is negative. | ||
| """ | ||
| if tol < 0: | ||
| raise ValueError(f"Tolerance must be non-negative, got {tol}") | ||
| self.tol = tol | ||
|
|
||
|
|
||
| class IpoptOptimizer(Optimizer): | ||
| """Use Ipopt (https://pypi.org/project/cyipopt/) for optimization.""" | ||
|
|
@@ -822,9 +892,7 @@ def set_maxtime(self, seconds: float) -> None: | |
| ) | ||
| if self.options is None: | ||
| self.options = {} | ||
| # We explicitly cast to float, as the IpoptOptimizer requires | ||
| # the provision of a float for the max_wall_time option. | ||
| self.options["max_wall_time"] = float(seconds) | ||
|
Comment on lines
-825
to
-827
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why removed?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that should not have happened. I think something got mixed up when i merged develop... |
||
| self.options["max_wall_time"] = seconds | ||
|
|
||
| def supports_maxiter(self) -> bool: | ||
| """Check whether optimizer supports iteration limits.""" | ||
|
|
@@ -843,6 +911,23 @@ def set_maxiter(self, iterations: int) -> None: | |
| self.options = {} | ||
| self.options["max_iter"] = iterations | ||
|
|
||
| def supports_f_abs_tol(self) -> bool: | ||
| """Check whether optimizer supports absolute function tolerance.""" | ||
| return False | ||
|
|
||
| def set_tol(self, tol: float) -> None: | ||
| """ | ||
| Set the convergence tolerance. | ||
|
|
||
| See https://coin-or.github.io/Ipopt/OPTIONS.html for more information. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tol | ||
| Tolerance value for termination. | ||
| """ | ||
| self._set_option_tol(tol, "tol") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From what I remember, ipopt has quite complex termination criteria. While various tolerances are supported, I think just hitting this single value is insufficient for termination, so it might be a bit confusing. Not completely sure whether it should be added here or not.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, it says so in the IPOPT documentation with a quite lengthy passage. I would in general be fine with removing it, but i am not sure how to handle the |
||
|
|
||
|
|
||
| class DlibOptimizer(Optimizer): | ||
| """Use the Dlib toolbox for optimization.""" | ||
|
|
@@ -966,6 +1051,10 @@ def set_maxiter(self, iterations: int) -> None: | |
| self.options = {} | ||
| self.options["maxiter"] = iterations | ||
|
|
||
| def supports_f_abs_tol(self) -> bool: | ||
| """Check whether optimizer supports absolute tolerance.""" | ||
| return False | ||
|
|
||
|
|
||
| class PyswarmOptimizer(Optimizer): | ||
| """Global optimization using pyswarm.""" | ||
|
|
@@ -1041,6 +1130,17 @@ def set_maxiter(self, iterations: int) -> None: | |
| self.options = {} | ||
| self.options["maxiter"] = iterations | ||
|
|
||
| def set_f_abs_tol(self, tol: float) -> None: | ||
| """ | ||
| Set the absolute tolerance for optimization. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tol | ||
| Absolute tolerance for termination. | ||
| """ | ||
| self._set_option_tol(tol, "minfunc") | ||
|
|
||
|
|
||
| class CmaOptimizer(Optimizer): | ||
| """ | ||
|
|
@@ -1162,6 +1262,17 @@ def set_maxeval(self, evaluations: int) -> None: | |
| self.options = {} | ||
| self.options["maxfevals"] = evaluations | ||
|
|
||
| def set_f_abs_tol(self, tol: float) -> None: | ||
| """ | ||
| Set the absolute tolerance for optimization. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tol | ||
| Absolute tolerance for termination. | ||
| """ | ||
| self._set_option_tol(tol, "tolfun") | ||
|
|
||
|
|
||
| class CmaesOptimizer(CmaOptimizer): | ||
| """Deprecated, use CmaOptimizer instead.""" | ||
|
|
@@ -1263,6 +1374,17 @@ def set_maxiter(self, iterations: int) -> None: | |
| self.options = {} | ||
| self.options["maxiter"] = iterations | ||
|
|
||
| def set_f_abs_tol(self, tol: float) -> None: | ||
| """ | ||
| Set the absolute tolerance for optimization. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tol | ||
| Absolute tolerance for termination. | ||
| """ | ||
| self._set_option_tol(tol, "atol") | ||
|
|
||
|
|
||
| class PyswarmsOptimizer(Optimizer): | ||
| """ | ||
|
|
@@ -1404,6 +1526,10 @@ def set_maxiter(self, iterations: int) -> None: | |
| self.options = {} | ||
| self.options["maxiter"] = iterations | ||
|
|
||
| def supports_f_abs_tol(self) -> bool: | ||
| """Check whether optimizer supports absolute tolerance.""" | ||
| return False | ||
|
|
||
|
|
||
| class NLoptOptimizer(Optimizer): | ||
| """ | ||
|
|
@@ -1676,6 +1802,17 @@ def set_maxeval(self, evaluations: int) -> None: | |
| """ | ||
| self.options["maxeval"] = evaluations | ||
|
|
||
| def set_f_abs_tol(self, tol: float) -> None: | ||
| """ | ||
| Set the absolute tolerance for optimization. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tol | ||
| Absolute tolerance for termination. | ||
| """ | ||
| self._set_option_tol(tol, "ftol_abs") | ||
|
|
||
|
|
||
| class FidesOptimizer(Optimizer): | ||
| """ | ||
|
|
@@ -1900,3 +2037,19 @@ def set_maxiter(self, iterations: int) -> None: | |
| self.options[FidesOptions.MAXITER] = iterations | ||
| except ImportError: | ||
| raise OptimizerImportError("fides") from None | ||
|
|
||
| def set_f_abs_tol(self, tol: float) -> None: | ||
| """ | ||
| Set the absolute tolerance for optimization. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| tol | ||
| Absolute tolerance for termination. | ||
| """ | ||
| try: | ||
| from fides.constants import Options as FidesOptions | ||
|
|
||
| self._set_option_tol(tol, FidesOptions.FATOL) | ||
| except ImportError: | ||
| raise OptimizerImportError("fides") from None | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class Optimizerdoes not have anoptionsattribute, we should not try accessing it here. I'd leave validation to the optimizer and just set the options directly in each optimizer without this extra method.