Skip to content

Commit db42f07

Browse files
committed
feat: expose supported advanced Acados options
1 parent e8fbc72 commit db42f07

2 files changed

Lines changed: 152 additions & 15 deletions

File tree

bioptim/interfaces/acados_options.py

Lines changed: 106 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
Int,
1111
IntorFloat,
1212
Float,
13+
FloatOptional,
14+
IntOptional,
1315
StrOptional,
1416
StrList,
1517
)
@@ -86,6 +88,18 @@ class ACADOS(GenericSolver):
8688
_sim_method_newton_iter: Int = 5
8789
_sim_method_num_stages: Int = 4
8890
_sim_method_num_steps: Int = 1
91+
_collocation_type: Str = "GAUSS_LEGENDRE"
92+
_sim_method_newton_tol: FloatOptional = None
93+
_sim_method_jac_reuse: Int = 0
94+
_qp_solver_cond_N: IntOptional = None
95+
_qp_solver_iter_max: Int = 50
96+
_qp_solver_tol_stat: FloatOptional = None
97+
_qp_solver_tol_eq: FloatOptional = None
98+
_qp_solver_tol_ineq: FloatOptional = None
99+
_qp_solver_tol_comp: FloatOptional = None
100+
_regularize_method: Str = "NO_REGULARIZE"
101+
_levenberg_marquardt: Float = 0.0
102+
_globalization: Str = "FIXED_STEP"
89103
_print_level: Int = 1
90104
_cost_type: Str = "NONLINEAR_LS"
91105
_constr_type: Str = "BGH"
@@ -109,14 +123,7 @@ def set_option_unsafe(self, val: IntorFloat | Str, name: Str) -> None:
109123
This function is unsafe because we did not check if the option exist in the solver option list.
110124
If it's not it just will be ignored. Please make sure that the option you're asking for exist.
111125
"""
112-
if not hasattr(self, "__annotations__"):
113-
if hasattr(self, "__annotations_cache__"):
114-
self.__annotations__ = self.__annotations_cache__
115-
else:
116-
raise AttributeError("No annotations found for the class.")
117-
118-
if f"_{name}" not in self.__annotations__.keys():
119-
self.__annotations__[f"_{name}"] = val
126+
if f"_{name}" not in self.__dict__:
120127
self.__setattr__(f"_{name}", val)
121128
self.set_only_first_options_has_changed(True)
122129

@@ -168,6 +175,93 @@ def set_sim_method_num_steps(self, val: Int) -> None:
168175
self._sim_method_num_steps = val
169176
self.set_only_first_options_has_changed(True)
170177

178+
@property
179+
def collocation_type(self) -> Str:
180+
return self._collocation_type
181+
182+
def set_collocation_type(self, val: Str) -> None:
183+
self._collocation_type = val
184+
self.set_only_first_options_has_changed(True)
185+
186+
@property
187+
def sim_method_newton_tol(self) -> FloatOptional:
188+
return self._sim_method_newton_tol
189+
190+
def set_sim_method_newton_tol(self, val: Float) -> None:
191+
self._sim_method_newton_tol = val
192+
self.set_only_first_options_has_changed(True)
193+
194+
@property
195+
def sim_method_jac_reuse(self) -> Int:
196+
return self._sim_method_jac_reuse
197+
198+
def set_sim_method_jac_reuse(self, val: Int) -> None:
199+
self._sim_method_jac_reuse = val
200+
self.set_only_first_options_has_changed(True)
201+
202+
@property
203+
def qp_solver_cond_N(self) -> IntOptional:
204+
return self._qp_solver_cond_N
205+
206+
def set_qp_solver_cond_N(self, val: Int) -> None:
207+
self._qp_solver_cond_N = val
208+
self.set_only_first_options_has_changed(True)
209+
210+
@property
211+
def qp_solver_iter_max(self) -> Int:
212+
return self._qp_solver_iter_max
213+
214+
def set_qp_solver_iter_max(self, val: Int) -> None:
215+
self._qp_solver_iter_max = val
216+
self.set_only_first_options_has_changed(True)
217+
218+
@property
219+
def regularize_method(self) -> Str:
220+
return self._regularize_method
221+
222+
def set_regularize_method(self, val: Str) -> None:
223+
self._regularize_method = val
224+
self.set_only_first_options_has_changed(True)
225+
226+
@property
227+
def levenberg_marquardt(self) -> Float:
228+
return self._levenberg_marquardt
229+
230+
def set_levenberg_marquardt(self, val: Float) -> None:
231+
self._levenberg_marquardt = val
232+
self.set_only_first_options_has_changed(True)
233+
234+
@property
235+
def globalization(self) -> Str:
236+
return self._globalization
237+
238+
def set_globalization(self, val: Str) -> None:
239+
self._globalization = val
240+
self.set_only_first_options_has_changed(True)
241+
242+
def set_qp_solver_tolerances(self, val: Float) -> None:
243+
self._qp_solver_tol_stat = val
244+
self._qp_solver_tol_eq = val
245+
self._qp_solver_tol_ineq = val
246+
self._qp_solver_tol_comp = val
247+
self.set_only_first_options_has_changed(True)
248+
249+
@property
250+
def qp_solver_tol_stat(self) -> FloatOptional:
251+
return self._qp_solver_tol_stat
252+
253+
@property
254+
def qp_solver_tol_eq(self) -> FloatOptional:
255+
return self._qp_solver_tol_eq
256+
257+
@property
258+
def qp_solver_tol_ineq(self) -> FloatOptional:
259+
return self._qp_solver_tol_ineq
260+
261+
@property
262+
def qp_solver_tol_comp(self) -> FloatOptional:
263+
return self._qp_solver_tol_comp
264+
171265
@property
172266
def cost_type(self) -> Str:
173267
return self._cost_type
@@ -270,17 +364,14 @@ def as_dict(self, solver):
270364
}
271365

272366
# Select the set of relevant keys before entering the loop
273-
if not hasattr(self, "__annotations__"):
274-
if hasattr(self, "__annotations_cache__"):
275-
self.__annotations__ = self.__annotations_cache__
276-
else:
277-
raise AttributeError("No annotations found for the class.")
278-
relevant_keys = set(self.__annotations__.keys()) - keys_to_skip
367+
relevant_keys = set(self.__dict__) - keys_to_skip
279368

280369
# Iterate only over relevant keys
281370
for key in relevant_keys:
282371
option_key = key[1:] if key[0] == "_" else key
283-
options[option_key] = getattr(self, key)
372+
value = getattr(self, key)
373+
if value is not None:
374+
options[option_key] = value
284375

285376
return options
286377

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from bioptim import Solver
2+
3+
4+
def test_acados_advanced_options_are_explicitly_exposed():
5+
solver = Solver.ACADOS()
6+
solver.set_integrator_type("ERK")
7+
solver.set_collocation_type("GAUSS_RADAU_IIA")
8+
solver.set_sim_method_num_stages(3)
9+
solver.set_sim_method_num_steps(2)
10+
solver.set_sim_method_newton_iter(7)
11+
solver.set_sim_method_newton_tol(1e-9)
12+
solver.set_sim_method_jac_reuse(1)
13+
solver.set_qp_solver("FULL_CONDENSING_HPIPM")
14+
solver.set_qp_solver_cond_N(5)
15+
solver.set_qp_solver_iter_max(75)
16+
solver.set_qp_solver_tolerances(1e-7)
17+
solver.set_regularize_method("CONVEXIFY")
18+
solver.set_levenberg_marquardt(1e-4)
19+
solver.set_globalization("MERIT_BACKTRACKING")
20+
21+
options = solver.as_dict(None)
22+
assert options["integrator_type"] == "ERK"
23+
assert options["collocation_type"] == "GAUSS_RADAU_IIA"
24+
assert options["sim_method_num_stages"] == 3
25+
assert options["sim_method_num_steps"] == 2
26+
assert options["sim_method_newton_iter"] == 7
27+
assert options["sim_method_newton_tol"] == 1e-9
28+
assert options["sim_method_jac_reuse"] == 1
29+
assert options["qp_solver"] == "FULL_CONDENSING_HPIPM"
30+
assert options["qp_solver_cond_N"] == 5
31+
assert options["qp_solver_iter_max"] == 75
32+
assert options["qp_solver_tol_stat"] == 1e-7
33+
assert options["qp_solver_tol_eq"] == 1e-7
34+
assert options["qp_solver_tol_ineq"] == 1e-7
35+
assert options["qp_solver_tol_comp"] == 1e-7
36+
assert options["regularize_method"] == "CONVEXIFY"
37+
assert options["levenberg_marquardt"] == 1e-4
38+
assert options["globalization"] == "MERIT_BACKTRACKING"
39+
assert solver.only_first_options_has_changed
40+
41+
42+
def test_acados_optional_options_are_not_forwarded_until_configured():
43+
options = Solver.ACADOS().as_dict(None)
44+
assert "sim_method_newton_tol" not in options
45+
assert "qp_solver_cond_N" not in options
46+
assert "qp_solver_tol_stat" not in options

0 commit comments

Comments
 (0)