diff --git a/AGENTS.md b/AGENTS.md index ee7e30a..694b342 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 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 94d96b7..e7c2d85 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)* | -| `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_FALSE` | Enables the nonzero-curvature test. *(differs from qpOASES default)* | +| `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. | @@ -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..adbbd60 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,18 +98,14 @@ class qpOASES_Solver * values. * @see `Options::enableFlippingBounds`. Page 22 of the manual. */ - BooleanType enableFlippingBounds = BT_FALSE; + 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 @@ -133,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_FALSE; + 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.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/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 a28fd23..366b0f4 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -81,13 +81,13 @@ 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."); - 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;