From 63a161dbcfa0637683f10a07be73bbce8c6abf67 Mon Sep 17 00:00:00 2001 From: mmmarinho Date: Fri, 28 Aug 2026 09:12:18 +0000 Subject: [PATCH 1/2] Use qpOASES' own defaults for Configuration Set the remaining non-default `Configuration` fields to qpOASES' `Options::setToDefault()` values (double precision): - `print_level`: PL_LOW -> PL_MEDIUM - `enableFlippingBounds`: BT_FALSE -> BT_TRUE - `enableNZCTests`: BT_FALSE -> BT_TRUE (`enableRegularisation` was already BT_FALSE, which is qpOASES' default.) Examples now set only the options they genuinely need: `example.py`'s `semidefinite()` sets `hessian_type = HST_SEMIDEF` (its Hessian is rank-deficient); all other examples run on the pure defaults. All five example solves and `example_kinematics` still converge with identical solutions. Docs updated to state that defaults match qpOASES' own defaults: - `README.md` configuration reference and defaults table. - `AGENTS.md` conventions ("Defaults match qpOASES"). - header docstring and the `print_level` pybind11 docstring. Co-authored-by: openhands --- AGENTS.md | 33 +++++++++++++-------------- README.md | 16 +++++-------- include/qpOASES_solver.h | 23 +++++-------------- marinholab/solvers/qpoases/example.py | 6 ++--- src/core.cpp | 2 +- 5 files changed, 32 insertions(+), 48 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ee7e30a..1c255d4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,23 +73,22 @@ Configuration lives in `pyrightconfig.json` (`pythonVersion` 3.9, ## Conventions -- **Preserve prior solver behaviour.** The wrapper's historical defaults are - the behavioural contract. In particular: - - `Configuration.enableRegularisation` defaults to `BT_FALSE` - (qpOASES' own default is `BT_TRUE`) — do not "fix" it to `BT_TRUE` without - re-validating the semi-definite / indefinite examples. - - `Configuration.print_level` defaults to `PL_LOW` (qpOASES default is - `PL_MEDIUM`) so the solver is quiet by default. - - `Configuration.enableNZCTests` and `enableFlippingBounds` default to - `BT_FALSE` (qpOASES default is `BT_TRUE`) — the "fast"/MPC preset. - - `Solver` accepts `None` for `A`/`b`/`Aeq`/`beq` and substitutes a single - trivially-satisfied zero row; `Solver.get_active_set()` returns one entry - per combined constraint row (-1 lower / 0 inactive / +1 upper). -- **Style.** Match the existing style: no trailing-whitespace obsession, - docstrings on public API, snake_case for new `Configuration` fields that - map to qpOASES `Options` fields (keep qpOASES-native names where they are - already the established wrapper spelling: `enableFlippingBounds`, - `enableRegularisation`, `enableNZCTests`). +- **Defaults match qpOASES.** `Configuration` defaults mirror qpOASES' + own `Options::setToDefault()` for a double-precision build (see the + defaults table in `README.md`). Do not silently override them here; if a + particular problem needs a non-default option, set it on the + `Configuration` in the *caller* (e.g. `example.py:semidefinite()` sets + `hessian_type = HST_SEMIDEF` because its Hessian is rank-deficient). + Re-validate the `example.py` and `example_kinematics.py` solves after any + change to a default. +- **`Solver` API.** `Solver.solve_quadratic_program()` accepts `None` for + `A`/`b`/`Aeq`/`beq` and substitutes a single trivially-satisfied zero row; + `Solver.get_active_set()` returns one entry per combined constraint row + (-1 lower / 0 inactive / +1 upper). +- **Style.** Match the existing style: docstrings on the public API, + snake_case for `Configuration` fields that map to qpOASES `Options` fields + (keep qpOASES-native names where they are already the established wrapper + spelling: `enableFlippingBounds`, `enableRegularisation`, `enableNZCTests`). - **Doxygen.** C++ types and members are documented with Doxygen (`/** ... @brief ... @see ... */` blocks). Keep that when adding fields. - **Annotations.** All public Python API is fully type-annotated and must diff --git a/README.md b/README.md index 94d96b7..8ea0f70 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,6 @@ the solver: ```python config = qpoases.Configuration() config.hessian_type = qpoases.HessianType.HST_SEMIDEF # H is rank-deficient -config.enableRegularisation = qpoases.BooleanType.BT_FALSE config.termination_tolerance = 1.0e-9 # tighter convergence solver = qpoases.Solver(config) ``` @@ -111,12 +110,9 @@ The enum types are re-exported for convenience: `qpoases.BooleanType`, ### qpOASES options These map 1:1 onto qpOASES' `Options` fields. Defaults match qpOASES' own -defaults for a **double-precision** build (see `Options::setToDefault()`), with -two deliberate exceptions noted inline: `enableNZCTests` and -`enableFlippingBounds` default to `BT_FALSE` here (qpOASES' default is -`BT_TRUE`) because those are the recommended "fast"/MPC settings for repeated, -online solves. See the [qpOASES manual](https://www.coin-or.org/qpOASES/doc/3.0/manual.pdf) -for a full description of each option. +defaults for a **double-precision** build (see `Options::setToDefault()`). See +the [qpOASES manual](https://www.coin-or.org/qpOASES/doc/3.0/manual.pdf) for a +full description of each option. **Booleans** (`BooleanType`: `BT_FALSE` / `BT_TRUE`) @@ -124,10 +120,10 @@ for a full description of each option. |---|---|---| | `enable_ramping` | `BT_TRUE` | Enables the ramping strategy. | | `enable_far_bounds` | `BT_TRUE` | Enables the far bounds strategy. | -| `enableFlippingBounds` | `BT_FALSE` | Allows flipping active bounds between lower and upper values. *(differs from qpOASES default)* | +| `enableFlippingBounds` | `BT_TRUE` | Allows flipping active bounds between lower and upper values. | | `enableRegularisation` | `BT_FALSE` | Regularises `H` when (semi-)definiteness is detected. | | `enable_full_li_tests` | `BT_FALSE` | Uses the condition-hardened linear-independence (LI) test. | -| `enableNZCTests` | `BT_FALSE` | Enables the nonzero-curvature test. *(differs from qpOASES default)* | +| `enableNZCTests` | `BT_TRUE` | Enables the nonzero-curvature test. | | `enable_equalities` | `BT_FALSE` | Treats equality constraints as always active. | | `enable_inertia_correction` | `BT_TRUE` | Repairs the working set when negative curvature is found during a hotstart. | | `enable_drop_infeasibles` | `BT_FALSE` | Whether infeasible constraints may be dropped. | @@ -170,7 +166,7 @@ for a full description of each option. | Option | Default | Type | Description | |---|---|---|---| -| `print_level` | `PL_LOW` | `PrintLevel` | Verbosity of qpOASES output (`PL_NONE`, `PL_LOW`, `PL_MEDIUM`, `PL_HIGH`, `PL_TABULAR`, `PL_DEBUG_ITER`). Defaults to `PL_LOW` so the solver stays quiet (qpOASES' own default is `PL_MEDIUM`). | +| `print_level` | `PL_MEDIUM` | `PrintLevel` | Verbosity of qpOASES output (`PL_NONE`, `PL_LOW`, `PL_MEDIUM`, `PL_HIGH`, `PL_TABULAR`, `PL_DEBUG_ITER`). | | `initial_status_bounds` | `ST_LOWER` | `SubjectToStatus` | Status assumed for all bounds at the first iteration. | ### Print levels (`PrintLevel`) diff --git a/include/qpOASES_solver.h b/include/qpOASES_solver.h index c07a2ec..5a7a81a 100644 --- a/include/qpOASES_solver.h +++ b/include/qpOASES_solver.h @@ -38,17 +38,9 @@ class qpOASES_Solver /** * @brief Holds all user-configurable solver options. * - * Every qpOASES `Options` field is exposed here. Defaults are chosen - * to match qpOASES' own defaults for a double-precision build (see - * `Options::setToDefault()`), with three deliberate exceptions: - * - * - enableNZCTests defaults to BT_FALSE (qpOASES default is - * BT_TRUE) and enableFlippingBounds defaults to BT_FALSE - * (qpOASES default is BT_TRUE). These are the settings used by - * qpOASES' "fast"/MPC preset and are the recommended settings - * for online, embedded use. - * - print_level defaults to PL_LOW (qpOASES default is PL_MEDIUM) - * so the wrapper stays quiet by default. + * Every qpOASES `Options` field is exposed here. Defaults match + * qpOASES' own defaults for a double-precision build (see + * `Options::setToDefault()`). * * @note qpOASES applies `Options::ensureConsistency()` when it sets * its options, which will silently adjust any value that falls @@ -85,12 +77,9 @@ class qpOASES_Solver /** * @brief Verbose-ness of qpOASES output. - * - * Defaults to PL_LOW so the solver stays quiet, matching this - * wrapper's historical behaviour. * @see `Options::printLevel` */ - PrintLevel print_level = PL_LOW; + PrintLevel print_level = PL_MEDIUM; /** * @brief Whether the ramping strategy shall be used. @@ -109,7 +98,7 @@ class qpOASES_Solver * values. * @see `Options::enableFlippingBounds`. Page 22 of the manual. */ - BooleanType enableFlippingBounds = BT_FALSE; + BooleanType enableFlippingBounds = BT_TRUE; /** * @brief Whether the Hessian shall be regularised in case @@ -133,7 +122,7 @@ class qpOASES_Solver * @brief Whether nonzero curvature tests shall be used. * @see `Options::enableNZCTests`. Page 22 of the manual. */ - BooleanType enableNZCTests = BT_FALSE; + BooleanType enableNZCTests = BT_TRUE; /** * @brief Frequency of drift corrections (0 = off). diff --git a/marinholab/solvers/qpoases/example.py b/marinholab/solvers/qpoases/example.py index efd77e3..177c35d 100644 --- a/marinholab/solvers/qpoases/example.py +++ b/marinholab/solvers/qpoases/example.py @@ -61,11 +61,11 @@ def positivedefinite() -> None: print(u_both) def semidefinite() -> None: + # The Hessian here is positive semi-definite (rank deficient), so the + # Hessian type must be set to HST_SEMIDEF; everything else stays at the + # defaults. config = qpoases.Configuration() config.hessian_type = qpoases.HessianType.HST_SEMIDEF - config.enableRegularisation = qpoases.BooleanType.BT_FALSE - #config.enableNZCTests = qpOASES_Solver.BooleanType.BT_TRUE - #config.enableFlippingBounds = qpOASES_Solver.BooleanType.BT_TRUE solver = qpoases.Solver(config) x = np.array([1.0, 0.0, 0.0, 0.0]) diff --git a/src/core.cpp b/src/core.cpp index a28fd23..84be44c 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -81,7 +81,7 @@ PYBIND11_MODULE(_core, m) { qpoases_configuration.def_readwrite("maximum_working_set_recalculations", &qpOASES_Solver::Configuration::maximum_working_set_recalculations, "Maximum number of working set recalculations during the initial homotopy."); qpoases_configuration.def_readwrite("use_hotstart", &qpOASES_Solver::Configuration::use_hotstart, "Whether subsequent solves are warm-started instead of re-initialised."); // qpOASES `Options` fields - qpoases_configuration.def_readwrite("print_level", &qpOASES_Solver::Configuration::print_level, "qpOASES print level (default PL_LOW, quiet)."); + qpoases_configuration.def_readwrite("print_level", &qpOASES_Solver::Configuration::print_level, "qpOASES print level (default PL_MEDIUM)."); qpoases_configuration.def_readwrite("enable_ramping", &qpOASES_Solver::Configuration::enable_ramping, "Enables the ramping strategy."); qpoases_configuration.def_readwrite("enable_far_bounds", &qpOASES_Solver::Configuration::enable_far_bounds, "Enables the far bounds strategy."); qpoases_configuration.def_readwrite("enableFlippingBounds", &qpOASES_Solver::Configuration::enableFlippingBounds, "Enables flipping of active bounds between lower and upper values."); From 36cf944910017ea004e25137b5cfead6414a1dd0 Mon Sep 17 00:00:00 2001 From: mmmarinho Date: Wed, 2 Sep 2026 15:40:57 +0000 Subject: [PATCH 2/2] Rename remaining Configuration fields to snake_case All `Configuration` fields are now snake_case, including the three that had kept their qpOASES-native camelCase spelling: - `enableFlippingBounds` -> `enable_flipping_bounds` - `enableRegularisation` -> `enable_regularisation` - `enableNZCTests` -> `enable_nzc_tests` Updated in the C++ header, the `_to_qpoases_options()` mapping (the qpOASES `Options` field names on the left-hand side are the library's and unchanged), the pybind11 bindings, the `_core.pyi` stub, the examples (`example_kinematics.py` drops its now-redundant `enable_regularisation = BT_FALSE` line, which is already the default), the README options table, and the AGENTS.md style note. Co-authored-by: openhands --- AGENTS.md | 8 ++++---- README.md | 6 +++--- include/qpOASES_solver.h | 10 +++------- marinholab/solvers/qpoases/_core.pyi | 6 +++--- marinholab/solvers/qpoases/example_kinematics.py | 5 ++--- src/core.cpp | 6 +++--- src/core_function.cpp | 6 +++--- 7 files changed, 21 insertions(+), 26 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1c255d4..694b342 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -85,10 +85,10 @@ Configuration lives in `pyrightconfig.json` (`pythonVersion` 3.9, `A`/`b`/`Aeq`/`beq` and substitutes a single trivially-satisfied zero row; `Solver.get_active_set()` returns one entry per combined constraint row (-1 lower / 0 inactive / +1 upper). -- **Style.** Match the existing style: docstrings on the public API, - snake_case for `Configuration` fields that map to qpOASES `Options` fields - (keep qpOASES-native names where they are already the established wrapper - spelling: `enableFlippingBounds`, `enableRegularisation`, `enableNZCTests`). +- **Style.** Match the existing style: docstrings on the public API and + snake_case for every `Configuration` field (including the ones whose + qpOASES `Options` names are camelCase, e.g. `enable_flipping_bounds` for + `Options::enableFlippingBounds`). - **Doxygen.** C++ types and members are documented with Doxygen (`/** ... @brief ... @see ... */` blocks). Keep that when adding fields. - **Annotations.** All public Python API is fully type-annotated and must diff --git a/README.md b/README.md index 8ea0f70..e7c2d85 100644 --- a/README.md +++ b/README.md @@ -120,10 +120,10 @@ full description of each option. |---|---|---| | `enable_ramping` | `BT_TRUE` | Enables the ramping strategy. | | `enable_far_bounds` | `BT_TRUE` | Enables the far bounds strategy. | -| `enableFlippingBounds` | `BT_TRUE` | Allows flipping active bounds between lower and upper values. | -| `enableRegularisation` | `BT_FALSE` | Regularises `H` when (semi-)definiteness is detected. | +| `enable_flipping_bounds` | `BT_TRUE` | Allows flipping active bounds between lower and upper values. | +| `enable_regularisation` | `BT_FALSE` | Regularises `H` when (semi-)definiteness is detected. | | `enable_full_li_tests` | `BT_FALSE` | Uses the condition-hardened linear-independence (LI) test. | -| `enableNZCTests` | `BT_TRUE` | Enables the nonzero-curvature test. | +| `enable_nzc_tests` | `BT_TRUE` | Enables the nonzero-curvature test. | | `enable_equalities` | `BT_FALSE` | Treats equality constraints as always active. | | `enable_inertia_correction` | `BT_TRUE` | Repairs the working set when negative curvature is found during a hotstart. | | `enable_drop_infeasibles` | `BT_FALSE` | Whether infeasible constraints may be dropped. | diff --git a/include/qpOASES_solver.h b/include/qpOASES_solver.h index 5a7a81a..adbbd60 100644 --- a/include/qpOASES_solver.h +++ b/include/qpOASES_solver.h @@ -98,18 +98,14 @@ class qpOASES_Solver * values. * @see `Options::enableFlippingBounds`. Page 22 of the manual. */ - BooleanType enableFlippingBounds = BT_TRUE; + BooleanType enable_flipping_bounds = BT_TRUE; /** * @brief Whether the Hessian shall be regularised in case * (semi-)definiteness is detected. - * - * Defaults to BT_FALSE to match both qpOASES' own default and the - * effective default of this wrapper prior to this option being - * passed through. * @see `Options::enableRegularisation`. Page 26 of the manual. */ - BooleanType enableRegularisation = BT_FALSE; + BooleanType enable_regularisation = BT_FALSE; /** * @brief Whether the condition-hardened linear independence @@ -122,7 +118,7 @@ class qpOASES_Solver * @brief Whether nonzero curvature tests shall be used. * @see `Options::enableNZCTests`. Page 22 of the manual. */ - BooleanType enableNZCTests = BT_TRUE; + BooleanType enable_nzc_tests = BT_TRUE; /** * @brief Frequency of drift corrections (0 = off). diff --git a/marinholab/solvers/qpoases/_core.pyi b/marinholab/solvers/qpoases/_core.pyi index 75ea716..ebf511c 100644 --- a/marinholab/solvers/qpoases/_core.pyi +++ b/marinholab/solvers/qpoases/_core.pyi @@ -83,13 +83,13 @@ class qpOASES_Solver: #: Enables the far bounds strategy. enable_far_bounds: BooleanType #: Enables flipping of active bounds between lower and upper values. - enableFlippingBounds: BooleanType + enable_flipping_bounds: BooleanType #: Regularises the Hessian in case (semi-)definiteness is detected. - enableRegularisation: BooleanType + enable_regularisation: BooleanType #: Uses the condition-hardened linear independence test. enable_full_li_tests: BooleanType #: Enables the nonzero curvature test. - enableNZCTests: BooleanType + enable_nzc_tests: BooleanType #: Frequency of drift corrections (0 = off). enable_drift_correction: int #: Frequency of full Cholesky refactorisation of the projected Hessian (0 = updates only). diff --git a/marinholab/solvers/qpoases/example_kinematics.py b/marinholab/solvers/qpoases/example_kinematics.py index afda005..875e49f 100644 --- a/marinholab/solvers/qpoases/example_kinematics.py +++ b/marinholab/solvers/qpoases/example_kinematics.py @@ -67,11 +67,10 @@ def main(): # qpOASES is an active-set solver and its Hessian must instead be flagged # according to its definiteness. Level 1's H1 = Jt.T @ Jt is only # positive semi-definite (rank <= 3 out of 7 joints), so we flag it as - # such and disable qpOASES' automatic regularisation, matching the - # `semidefinite()` pattern in `example.py`. + # such; everything else stays at the defaults (in particular + # `enable_regularisation` is already BT_FALSE). config_1 = qpoases.Configuration() config_1.hessian_type = qpoases.HessianType.HST_SEMIDEF - config_1.enableRegularisation = qpoases.BooleanType.BT_FALSE # Level 2's H2 = I is positive definite, so the default configuration # (HST_POSDEF) is adequate. diff --git a/src/core.cpp b/src/core.cpp index 84be44c..366b0f4 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -84,10 +84,10 @@ PYBIND11_MODULE(_core, m) { qpoases_configuration.def_readwrite("print_level", &qpOASES_Solver::Configuration::print_level, "qpOASES print level (default PL_MEDIUM)."); qpoases_configuration.def_readwrite("enable_ramping", &qpOASES_Solver::Configuration::enable_ramping, "Enables the ramping strategy."); qpoases_configuration.def_readwrite("enable_far_bounds", &qpOASES_Solver::Configuration::enable_far_bounds, "Enables the far bounds strategy."); - qpoases_configuration.def_readwrite("enableFlippingBounds", &qpOASES_Solver::Configuration::enableFlippingBounds, "Enables flipping of active bounds between lower and upper values."); - qpoases_configuration.def_readwrite("enableRegularisation", &qpOASES_Solver::Configuration::enableRegularisation, "Regularises the Hessian in case (semi-)definiteness is detected."); + qpoases_configuration.def_readwrite("enable_flipping_bounds", &qpOASES_Solver::Configuration::enable_flipping_bounds, "Enables flipping of active bounds between lower and upper values."); + qpoases_configuration.def_readwrite("enable_regularisation", &qpOASES_Solver::Configuration::enable_regularisation, "Regularises the Hessian in case (semi-)definiteness is detected."); qpoases_configuration.def_readwrite("enable_full_li_tests", &qpOASES_Solver::Configuration::enable_full_li_tests, "Uses the condition-hardened linear independence test."); - qpoases_configuration.def_readwrite("enableNZCTests", &qpOASES_Solver::Configuration::enableNZCTests, "Enables the nonzero curvature test."); + qpoases_configuration.def_readwrite("enable_nzc_tests", &qpOASES_Solver::Configuration::enable_nzc_tests, "Enables the nonzero curvature test."); qpoases_configuration.def_readwrite("enable_drift_correction", &qpOASES_Solver::Configuration::enable_drift_correction, "Frequency of drift corrections (0 = off)."); qpoases_configuration.def_readwrite("enable_cholesky_refactorisation", &qpOASES_Solver::Configuration::enable_cholesky_refactorisation, "Frequency of full Cholesky refactorisation of the projected Hessian (0 = updates only)."); qpoases_configuration.def_readwrite("enable_equalities", &qpOASES_Solver::Configuration::enable_equalities, "Treats equality constraints as always active."); diff --git a/src/core_function.cpp b/src/core_function.cpp index 3019557..fc7b1bb 100644 --- a/src/core_function.cpp +++ b/src/core_function.cpp @@ -23,10 +23,10 @@ Options qpOASES_Solver::_to_qpoases_options() const options.printLevel = configuration_.print_level; options.enableRamping = configuration_.enable_ramping; options.enableFarBounds = configuration_.enable_far_bounds; - options.enableFlippingBounds = configuration_.enableFlippingBounds; - options.enableRegularisation = configuration_.enableRegularisation; + options.enableFlippingBounds = configuration_.enable_flipping_bounds; + options.enableRegularisation = configuration_.enable_regularisation; options.enableFullLITests = configuration_.enable_full_li_tests; - options.enableNZCTests = configuration_.enableNZCTests; + options.enableNZCTests = configuration_.enable_nzc_tests; options.enableDriftCorrection = configuration_.enable_drift_correction; options.enableCholeskyRefactorisation = configuration_.enable_cholesky_refactorisation; options.enableEqualities = configuration_.enable_equalities;