sleipnirgroup-jormungandr 0.0.1.dev475__tar.gz → 0.0.1.dev477__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/CMakeLists.txt +9 -0
  2. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/PKG-INFO +1 -1
  3. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/expression.hpp +41 -81
  4. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/optimization/solver/exit_status.hpp +11 -6
  5. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/optimization/solver/interior_point.hpp +5 -1
  6. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/docstrings.hpp +4 -0
  7. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/optimization/solver/bind_exit_status.cpp +2 -0
  8. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/nonlinear_problem_test.py +19 -0
  9. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/pyproject.toml +1 -1
  10. sleipnirgroup_jormungandr-0.0.1.dev477/src/optimization/bounds.hpp +220 -0
  11. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/problem.cpp +19 -1
  12. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/solver/interior_point.cpp +9 -1
  13. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/util/print_diagnostics.hpp +19 -0
  14. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/LICENSE.txt +0 -0
  15. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/README.md +0 -0
  16. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/SleipnirConfig.cmake.in +0 -0
  17. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/cmake/modules/BuildTypes.cmake +0 -0
  18. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/cmake/modules/CompilerFlags.cmake +0 -0
  19. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/cmake/modules/Pybind11Mkdoc.cmake +0 -0
  20. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/cmake/modules/SubdirList.cmake +0 -0
  21. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/.styleguide +0 -0
  22. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/adjoint_expression_graph.hpp +0 -0
  23. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/expression_graph.hpp +0 -0
  24. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/expression_type.hpp +0 -0
  25. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/gradient.hpp +0 -0
  26. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/hessian.hpp +0 -0
  27. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/jacobian.hpp +0 -0
  28. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/slice.hpp +0 -0
  29. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/variable.hpp +0 -0
  30. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/variable_block.hpp +0 -0
  31. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/autodiff/variable_matrix.hpp +0 -0
  32. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/control/ocp.hpp +0 -0
  33. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/optimization/multistart.hpp +0 -0
  34. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/optimization/problem.hpp +0 -0
  35. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/optimization/solver/iteration_info.hpp +0 -0
  36. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/optimization/solver/newton.hpp +0 -0
  37. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/optimization/solver/options.hpp +0 -0
  38. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/optimization/solver/sqp.hpp +0 -0
  39. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/util/assert.hpp +0 -0
  40. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/util/concepts.hpp +0 -0
  41. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/util/function_ref.hpp +0 -0
  42. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/util/intrusive_shared_ptr.hpp +0 -0
  43. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/util/pool.hpp +0 -0
  44. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/util/print.hpp +0 -0
  45. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/util/spy.hpp +0 -0
  46. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/include/sleipnir/util/symbol_exports.hpp +0 -0
  47. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/__init__.py +0 -0
  48. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/autodiff/__init__.py +0 -0
  49. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/control/__init__.py +0 -0
  50. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/autodiff/bind_expression_type.cpp +0 -0
  51. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/autodiff/bind_gradient.cpp +0 -0
  52. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/autodiff/bind_hessian.cpp +0 -0
  53. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/autodiff/bind_jacobian.cpp +0 -0
  54. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/autodiff/bind_variable.cpp +0 -0
  55. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/autodiff/bind_variable_block.cpp +0 -0
  56. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/autodiff/bind_variable_matrix.cpp +0 -0
  57. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/binders.hpp +0 -0
  58. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/control/bind_ocp.cpp +0 -0
  59. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/main.cpp +0 -0
  60. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/optimization/bind_equality_constraints.cpp +0 -0
  61. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/optimization/bind_inequality_constraints.cpp +0 -0
  62. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/optimization/bind_problem.cpp +0 -0
  63. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/optimization/solver/bind_iteration_info.cpp +0 -0
  64. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/cpp/try_cast.hpp +0 -0
  65. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/optimization/__init__.py +0 -0
  66. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/autodiff/gradient_test.py +0 -0
  67. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/autodiff/hessian_test.py +0 -0
  68. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/autodiff/jacobian_test.py +0 -0
  69. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/autodiff/variable_matrix_test.py +0 -0
  70. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/autodiff/variable_test.py +0 -0
  71. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/cart_pole_util.py +0 -0
  72. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/control/cart_pole_ocp_test.py +0 -0
  73. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/control/differential_drive_ocp_test.py +0 -0
  74. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/control/flywheel_ocp_test.py +0 -0
  75. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/differential_drive_util.py +0 -0
  76. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/arm_on_elevator_problem_test.py +0 -0
  77. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/cart_pole_problem_test.py +0 -0
  78. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/constraints_test.py +0 -0
  79. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/decision_variable_test.py +0 -0
  80. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/differential_drive_problem_test.py +0 -0
  81. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/double_integrator_problem_test.py +0 -0
  82. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/exit_status_test.py +0 -0
  83. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/flywheel_problem_test.py +0 -0
  84. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/linear_problem_test.py +0 -0
  85. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/multistart_test.py +0 -0
  86. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/quadratic_problem_test.py +0 -0
  87. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/optimization/trivial_problem_test.py +0 -0
  88. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/jormungandr/test/rk4.py +0 -0
  89. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/.styleguide +0 -0
  90. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/autodiff/variable_matrix.cpp +0 -0
  91. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/inertia.hpp +0 -0
  92. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/regularized_ldlt.hpp +0 -0
  93. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/solver/newton.cpp +0 -0
  94. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/solver/sqp.cpp +0 -0
  95. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/solver/util/error_estimate.hpp +0 -0
  96. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/solver/util/filter.hpp +0 -0
  97. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/solver/util/fraction_to_the_boundary_rule.hpp +0 -0
  98. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/solver/util/is_locally_infeasible.hpp +0 -0
  99. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/optimization/solver/util/kkt_error.hpp +0 -0
  100. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/util/pool.cpp +0 -0
  101. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/util/scope_exit.hpp +0 -0
  102. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/util/scoped_profiler.hpp +0 -0
  103. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/util/setup_profiler.hpp +0 -0
  104. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev477}/src/util/solve_profiler.hpp +0 -0
@@ -71,6 +71,11 @@ option(BUILD_BENCHMARKS "Build CasADi and Sleipnir benchmarks" OFF)
71
71
  option(BUILD_EXAMPLES "Build examples" OFF)
72
72
  option(BUILD_PYTHON "Build Python module" OFF)
73
73
  option(DISABLE_DIAGNOSTICS "Disable diagnostics support at compile-time" OFF)
74
+ option(
75
+ ENABLE_BOUND_PROJECTION
76
+ "Enable projecting the state onto bound inequality constraints inside problem setup"
77
+ OFF
78
+ )
74
79
 
75
80
  include(CompilerFlags)
76
81
 
@@ -104,6 +109,10 @@ if(DISABLE_DIAGNOSTICS)
104
109
  target_compile_definitions(Sleipnir PUBLIC SLEIPNIR_DISABLE_DIAGNOSTICS)
105
110
  endif()
106
111
 
112
+ if(ENABLE_BOUND_PROJECTION)
113
+ target_compile_definitions(Sleipnir PUBLIC SLEIPNIR_ENABLE_BOUND_PROJECTION)
114
+ endif()
115
+
107
116
  # Eigen dependency
108
117
  if(NOT USE_SYSTEM_EIGEN)
109
118
  set(EIGEN_BUILD_CMAKE_PACKAGE TRUE)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sleipnirgroup-jormungandr
3
- Version: 0.0.1.dev475
3
+ Version: 0.0.1.dev477
4
4
  Summary: A linearity-exploiting sparse nonlinear constrained optimization problem solver that uses the interior-point method.
5
5
  License: Copyright (c) Sleipnir contributors
6
6
 
@@ -436,9 +436,7 @@ struct BinaryMinusExpression final : Expression {
436
436
  * @param rhs Binary operator's right operand.
437
437
  */
438
438
  constexpr BinaryMinusExpression(ExpressionPtr lhs, ExpressionPtr rhs)
439
- : Expression{std::move(lhs), std::move(rhs)} {
440
- val = args[0]->val - args[1]->val;
441
- }
439
+ : Expression{std::move(lhs), std::move(rhs)} {}
442
440
 
443
441
  double value(double lhs, double rhs) const override { return lhs - rhs; }
444
442
 
@@ -479,9 +477,7 @@ struct BinaryPlusExpression final : Expression {
479
477
  * @param rhs Binary operator's right operand.
480
478
  */
481
479
  constexpr BinaryPlusExpression(ExpressionPtr lhs, ExpressionPtr rhs)
482
- : Expression{std::move(lhs), std::move(rhs)} {
483
- val = args[0]->val + args[1]->val;
484
- }
480
+ : Expression{std::move(lhs), std::move(rhs)} {}
485
481
 
486
482
  double value(double lhs, double rhs) const override { return lhs + rhs; }
487
483
 
@@ -565,9 +561,7 @@ struct DivExpression final : Expression {
565
561
  * @param rhs Binary operator's right operand.
566
562
  */
567
563
  constexpr DivExpression(ExpressionPtr lhs, ExpressionPtr rhs)
568
- : Expression{std::move(lhs), std::move(rhs)} {
569
- val = args[0]->val / args[1]->val;
570
- }
564
+ : Expression{std::move(lhs), std::move(rhs)} {}
571
565
 
572
566
  double value(double lhs, double rhs) const override { return lhs / rhs; }
573
567
 
@@ -608,9 +602,7 @@ struct MultExpression final : Expression {
608
602
  * @param rhs Binary operator's right operand.
609
603
  */
610
604
  constexpr MultExpression(ExpressionPtr lhs, ExpressionPtr rhs)
611
- : Expression{std::move(lhs), std::move(rhs)} {
612
- val = args[0]->val * args[1]->val;
613
- }
605
+ : Expression{std::move(lhs), std::move(rhs)} {}
614
606
 
615
607
  double value(double lhs, double rhs) const override { return lhs * rhs; }
616
608
 
@@ -652,9 +644,7 @@ struct UnaryMinusExpression final : Expression {
652
644
  * @param lhs Unary operator's operand.
653
645
  */
654
646
  explicit constexpr UnaryMinusExpression(ExpressionPtr lhs)
655
- : Expression{std::move(lhs)} {
656
- val = -args[0]->val;
657
- }
647
+ : Expression{std::move(lhs)} {}
658
648
 
659
649
  double value(double lhs, double) const override { return -lhs; }
660
650
 
@@ -735,9 +725,7 @@ struct AbsExpression final : Expression {
735
725
  * @param lhs Unary operator's operand.
736
726
  */
737
727
  explicit constexpr AbsExpression(ExpressionPtr lhs)
738
- : Expression{std::move(lhs)} {
739
- val = args[0]->val < 0 ? -args[0]->val : args[0]->val;
740
- }
728
+ : Expression{std::move(lhs)} {}
741
729
 
742
730
  double value(double x, double) const override { return std::abs(x); }
743
731
 
@@ -798,9 +786,8 @@ struct AcosExpression final : Expression {
798
786
  *
799
787
  * @param lhs Unary operator's operand.
800
788
  */
801
- explicit AcosExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
802
- val = std::acos(args[0]->val);
803
- }
789
+ explicit constexpr AcosExpression(ExpressionPtr lhs)
790
+ : Expression{std::move(lhs)} {}
804
791
 
805
792
  double value(double x, double) const override { return std::acos(x); }
806
793
 
@@ -848,9 +835,8 @@ struct AsinExpression final : Expression {
848
835
  *
849
836
  * @param lhs Unary operator's operand.
850
837
  */
851
- explicit AsinExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
852
- val = std::asin(args[0]->val);
853
- }
838
+ explicit constexpr AsinExpression(ExpressionPtr lhs)
839
+ : Expression{std::move(lhs)} {}
854
840
 
855
841
  double value(double x, double) const override { return std::asin(x); }
856
842
 
@@ -899,9 +885,8 @@ struct AtanExpression final : Expression {
899
885
  *
900
886
  * @param lhs Unary operator's operand.
901
887
  */
902
- explicit AtanExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
903
- val = std::atan(args[0]->val);
904
- }
888
+ explicit constexpr AtanExpression(ExpressionPtr lhs)
889
+ : Expression{std::move(lhs)} {}
905
890
 
906
891
  double value(double x, double) const override { return std::atan(x); }
907
892
 
@@ -950,10 +935,8 @@ struct Atan2Expression final : Expression {
950
935
  * @param lhs Binary operator's left operand.
951
936
  * @param rhs Binary operator's right operand.
952
937
  */
953
- Atan2Expression(ExpressionPtr lhs, ExpressionPtr rhs)
954
- : Expression{std::move(lhs), std::move(rhs)} {
955
- val = std::atan2(args[0]->val, args[1]->val);
956
- }
938
+ constexpr Atan2Expression(ExpressionPtr lhs, ExpressionPtr rhs)
939
+ : Expression{std::move(lhs), std::move(rhs)} {}
957
940
 
958
941
  double value(double y, double x) const override { return std::atan2(y, x); }
959
942
 
@@ -1014,9 +997,8 @@ struct CosExpression final : Expression {
1014
997
  *
1015
998
  * @param lhs Unary operator's operand.
1016
999
  */
1017
- explicit CosExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1018
- val = std::cos(args[0]->val);
1019
- }
1000
+ explicit constexpr CosExpression(ExpressionPtr lhs)
1001
+ : Expression{std::move(lhs)} {}
1020
1002
 
1021
1003
  double value(double x, double) const override { return std::cos(x); }
1022
1004
 
@@ -1063,9 +1045,8 @@ struct CoshExpression final : Expression {
1063
1045
  *
1064
1046
  * @param lhs Unary operator's operand.
1065
1047
  */
1066
- explicit CoshExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1067
- val = std::cosh(args[0]->val);
1068
- }
1048
+ explicit constexpr CoshExpression(ExpressionPtr lhs)
1049
+ : Expression{std::move(lhs)} {}
1069
1050
 
1070
1051
  double value(double x, double) const override { return std::cosh(x); }
1071
1052
 
@@ -1112,9 +1093,8 @@ struct ErfExpression final : Expression {
1112
1093
  *
1113
1094
  * @param lhs Unary operator's operand.
1114
1095
  */
1115
- explicit ErfExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1116
- val = std::erf(args[0]->val);
1117
- }
1096
+ explicit constexpr ErfExpression(ExpressionPtr lhs)
1097
+ : Expression{std::move(lhs)} {}
1118
1098
 
1119
1099
  double value(double x, double) const override { return std::erf(x); }
1120
1100
 
@@ -1165,9 +1145,8 @@ struct ExpExpression final : Expression {
1165
1145
  *
1166
1146
  * @param lhs Unary operator's operand.
1167
1147
  */
1168
- explicit ExpExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1169
- val = std::exp(args[0]->val);
1170
- }
1148
+ explicit constexpr ExpExpression(ExpressionPtr lhs)
1149
+ : Expression{std::move(lhs)} {}
1171
1150
 
1172
1151
  double value(double x, double) const override { return std::exp(x); }
1173
1152
 
@@ -1217,10 +1196,8 @@ struct HypotExpression final : Expression {
1217
1196
  * @param lhs Binary operator's left operand.
1218
1197
  * @param rhs Binary operator's right operand.
1219
1198
  */
1220
- HypotExpression(ExpressionPtr lhs, ExpressionPtr rhs)
1221
- : Expression{std::move(lhs), std::move(rhs)} {
1222
- val = std::hypot(args[0]->val, args[1]->val);
1223
- }
1199
+ constexpr HypotExpression(ExpressionPtr lhs, ExpressionPtr rhs)
1200
+ : Expression{std::move(lhs), std::move(rhs)} {}
1224
1201
 
1225
1202
  double value(double x, double y) const override { return std::hypot(x, y); }
1226
1203
 
@@ -1280,9 +1257,8 @@ struct LogExpression final : Expression {
1280
1257
  *
1281
1258
  * @param lhs Unary operator's operand.
1282
1259
  */
1283
- explicit LogExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1284
- val = std::log(args[0]->val);
1285
- }
1260
+ explicit constexpr LogExpression(ExpressionPtr lhs)
1261
+ : Expression{std::move(lhs)} {}
1286
1262
 
1287
1263
  double value(double x, double) const override { return std::log(x); }
1288
1264
 
@@ -1330,9 +1306,8 @@ struct Log10Expression final : Expression {
1330
1306
  *
1331
1307
  * @param lhs Unary operator's operand.
1332
1308
  */
1333
- explicit Log10Expression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1334
- val = std::log10(args[0]->val);
1335
- }
1309
+ explicit constexpr Log10Expression(ExpressionPtr lhs)
1310
+ : Expression{std::move(lhs)} {}
1336
1311
 
1337
1312
  double value(double x, double) const override { return std::log10(x); }
1338
1313
 
@@ -1387,10 +1362,8 @@ struct PowExpression final : Expression {
1387
1362
  * @param lhs Binary operator's left operand.
1388
1363
  * @param rhs Binary operator's right operand.
1389
1364
  */
1390
- PowExpression(ExpressionPtr lhs, ExpressionPtr rhs)
1391
- : Expression{std::move(lhs), std::move(rhs)} {
1392
- val = std::pow(args[0]->val, args[1]->val);
1393
- }
1365
+ constexpr PowExpression(ExpressionPtr lhs, ExpressionPtr rhs)
1366
+ : Expression{std::move(lhs), std::move(rhs)} {}
1394
1367
 
1395
1368
  double value(double base, double power) const override {
1396
1369
  return std::pow(base, power);
@@ -1489,15 +1462,7 @@ struct SignExpression final : Expression {
1489
1462
  * @param lhs Unary operator's operand.
1490
1463
  */
1491
1464
  explicit constexpr SignExpression(ExpressionPtr lhs)
1492
- : Expression{std::move(lhs)} {
1493
- if (args[0]->val < 0.0) {
1494
- val = -1.0;
1495
- } else if (args[0]->val == 0.0) {
1496
- val = 0.0;
1497
- } else {
1498
- val = 1.0;
1499
- }
1500
- }
1465
+ : Expression{std::move(lhs)} {}
1501
1466
 
1502
1467
  double value(double x, double) const override {
1503
1468
  if (x < 0.0) {
@@ -1552,9 +1517,8 @@ struct SinExpression final : Expression {
1552
1517
  *
1553
1518
  * @param lhs Unary operator's operand.
1554
1519
  */
1555
- explicit SinExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1556
- val = std::sin(args[0]->val);
1557
- }
1520
+ explicit constexpr SinExpression(ExpressionPtr lhs)
1521
+ : Expression{std::move(lhs)} {}
1558
1522
 
1559
1523
  double value(double x, double) const override { return std::sin(x); }
1560
1524
 
@@ -1602,9 +1566,8 @@ struct SinhExpression final : Expression {
1602
1566
  *
1603
1567
  * @param lhs Unary operator's operand.
1604
1568
  */
1605
- explicit SinhExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1606
- val = std::sinh(args[0]->val);
1607
- }
1569
+ explicit constexpr SinhExpression(ExpressionPtr lhs)
1570
+ : Expression{std::move(lhs)} {}
1608
1571
 
1609
1572
  double value(double x, double) const override { return std::sinh(x); }
1610
1573
 
@@ -1652,9 +1615,8 @@ struct SqrtExpression final : Expression {
1652
1615
  *
1653
1616
  * @param lhs Unary operator's operand.
1654
1617
  */
1655
- explicit SqrtExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1656
- val = std::sqrt(args[0]->val);
1657
- }
1618
+ explicit constexpr SqrtExpression(ExpressionPtr lhs)
1619
+ : Expression{std::move(lhs)} {}
1658
1620
 
1659
1621
  double value(double x, double) const override { return std::sqrt(x); }
1660
1622
 
@@ -1704,9 +1666,8 @@ struct TanExpression final : Expression {
1704
1666
  *
1705
1667
  * @param lhs Unary operator's operand.
1706
1668
  */
1707
- explicit TanExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1708
- val = std::tan(args[0]->val);
1709
- }
1669
+ explicit constexpr TanExpression(ExpressionPtr lhs)
1670
+ : Expression{std::move(lhs)} {}
1710
1671
 
1711
1672
  double value(double x, double) const override { return std::tan(x); }
1712
1673
 
@@ -1754,9 +1715,8 @@ struct TanhExpression final : Expression {
1754
1715
  *
1755
1716
  * @param lhs Unary operator's operand.
1756
1717
  */
1757
- explicit TanhExpression(ExpressionPtr lhs) : Expression{std::move(lhs)} {
1758
- val = std::tanh(args[0]->val);
1759
- }
1718
+ explicit constexpr TanhExpression(ExpressionPtr lhs)
1719
+ : Expression{std::move(lhs)} {}
1760
1720
 
1761
1721
  double value(double x, double) const override { return std::tanh(x); }
1762
1722
 
@@ -22,21 +22,24 @@ enum class ExitStatus : int8_t {
22
22
  TOO_FEW_DOFS = -1,
23
23
  /// The solver determined the problem to be locally infeasible and gave up.
24
24
  LOCALLY_INFEASIBLE = -2,
25
+ /// The problem setup frontend determined the problem to have an empty
26
+ /// feasible region.
27
+ GLOBALLY_INFEASIBLE = -3,
25
28
  /// The linear system factorization failed.
26
- FACTORIZATION_FAILED = -3,
29
+ FACTORIZATION_FAILED = -4,
27
30
  /// The backtracking line search failed, and the problem isn't locally
28
31
  /// infeasible.
29
- LINE_SEARCH_FAILED = -4,
32
+ LINE_SEARCH_FAILED = -5,
30
33
  /// The solver encountered nonfinite initial cost or constraints and gave up.
31
- NONFINITE_INITIAL_COST_OR_CONSTRAINTS = -5,
34
+ NONFINITE_INITIAL_COST_OR_CONSTRAINTS = -6,
32
35
  /// The solver encountered diverging primal iterates xₖ and/or sₖ and gave up.
33
- DIVERGING_ITERATES = -6,
36
+ DIVERGING_ITERATES = -7,
34
37
  /// The solver returned its solution so far after exceeding the maximum number
35
38
  /// of iterations.
36
- MAX_ITERATIONS_EXCEEDED = -7,
39
+ MAX_ITERATIONS_EXCEEDED = -8,
37
40
  /// The solver returned its solution so far after exceeding the maximum
38
41
  /// elapsed wall clock time.
39
- TIMEOUT = -8
42
+ TIMEOUT = -9,
40
43
  };
41
44
 
42
45
  /**
@@ -57,6 +60,8 @@ SLEIPNIR_DLLEXPORT constexpr std::string_view to_message(
57
60
  return "too few degrees of freedom";
58
61
  case LOCALLY_INFEASIBLE:
59
62
  return "locally infeasible";
63
+ case GLOBALLY_INFEASIBLE:
64
+ return "globally infeasible";
60
65
  case FACTORIZATION_FAILED:
61
66
  return "factorization failed";
62
67
  case LINE_SEARCH_FAILED:
@@ -223,6 +223,10 @@ SLEIPNIR_DLLEXPORT ExitStatus
223
223
  interior_point(const InteriorPointMatrixCallbacks& matrix_callbacks,
224
224
  std::span<std::function<bool(const IterationInfo& info)>>
225
225
  iteration_callbacks,
226
- const Options& options, Eigen::VectorXd& x);
226
+ const Options& options,
227
+ #ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
228
+ const Eigen::ArrayX<bool>& bound_constraint_mask,
229
+ #endif
230
+ Eigen::VectorXd& x);
227
231
 
228
232
  } // namespace slp
@@ -78,6 +78,10 @@ up.)doc";
78
78
 
79
79
  static const char *__doc_slp_ExitStatus_FACTORIZATION_FAILED = R"doc(The linear system factorization failed.)doc";
80
80
 
81
+ static const char *__doc_slp_ExitStatus_GLOBALLY_INFEASIBLE =
82
+ R"doc(The problem setup frontend determined the problem to have an empty
83
+ feasible region.)doc";
84
+
81
85
  static const char *__doc_slp_ExitStatus_LINE_SEARCH_FAILED =
82
86
  R"doc(The backtracking line search failed, and the problem isn't locally
83
87
  infeasible.)doc";
@@ -17,6 +17,8 @@ void bind_exit_status(nb::enum_<ExitStatus>& e) {
17
17
  DOC(slp, ExitStatus, TOO_FEW_DOFS));
18
18
  e.value("LOCALLY_INFEASIBLE", ExitStatus::LOCALLY_INFEASIBLE,
19
19
  DOC(slp, ExitStatus, LOCALLY_INFEASIBLE));
20
+ e.value("GLOBALLY_INFEASIBLE", ExitStatus::GLOBALLY_INFEASIBLE,
21
+ DOC(slp, ExitStatus, GLOBALLY_INFEASIBLE));
20
22
  e.value("FACTORIZATION_FAILED", ExitStatus::FACTORIZATION_FAILED,
21
23
  DOC(slp, ExitStatus, FACTORIZATION_FAILED));
22
24
  e.value("LINE_SEARCH_FAILED", ExitStatus::LINE_SEARCH_FAILED,
@@ -116,6 +116,25 @@ def test_minimum_2d_distance_with_linear_constraint():
116
116
  assert y.value() == pytest.approx(2.5, abs=1e-2)
117
117
 
118
118
 
119
+ def test_conflicting_bounds():
120
+ problem = Problem()
121
+
122
+ x = problem.decision_variable()
123
+ y = problem.decision_variable()
124
+
125
+ problem.minimize(autodiff.hypot(x, y))
126
+
127
+ problem.subject_to(autodiff.hypot(x, y) <= 1)
128
+ problem.subject_to(x >= 0.5)
129
+ problem.subject_to(x <= -0.5)
130
+
131
+ assert problem.cost_function_type() == ExpressionType.NONLINEAR
132
+ assert problem.equality_constraint_type() == ExpressionType.NONE
133
+ assert problem.inequality_constraint_type() == ExpressionType.NONLINEAR
134
+
135
+ assert problem.solve(diagnostics=True) == ExitStatus.GLOBALLY_INFEASIBLE
136
+
137
+
119
138
  def test_wachter_and_biegler_line_search_failure():
120
139
  # See example 19.2 of [1]
121
140
 
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "sleipnirgroup-jormungandr"
3
3
  description = "A linearity-exploiting sparse nonlinear constrained optimization problem solver that uses the interior-point method."
4
- version = "0.0.1.dev475"
4
+ version = "0.0.1.dev477"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
7
7
  dependencies = [ "matplotlib", "numpy", "scipy" ]
@@ -0,0 +1,220 @@
1
+ // Copyright (c) Sleipnir contributors
2
+
3
+ #pragma once
4
+
5
+ #include <algorithm>
6
+ #include <limits>
7
+ #include <span>
8
+ #include <utility>
9
+
10
+ #include <Eigen/Core>
11
+ #include <Eigen/SparseCore>
12
+ #include <gch/small_vector.hpp>
13
+
14
+ #include "sleipnir/autodiff/expression_type.hpp"
15
+ #include "sleipnir/autodiff/variable.hpp"
16
+ #include "sleipnir/util/assert.hpp"
17
+
18
+ // See docs/algorithms.md#Works_cited for citation definitions
19
+
20
+ namespace slp {
21
+
22
+ struct Bounds {
23
+ /// Which constraints, if any, are bound constraints.
24
+ Eigen::ArrayX<bool> bound_constraint_mask;
25
+
26
+ /// The tightest bounds on each decision variable.
27
+ gch::small_vector<std::pair<double, double>> bounds;
28
+
29
+ /// Whether or not the constraints are feasible (given previously encountered
30
+ /// bounds).
31
+ gch::small_vector<std::pair<Eigen::Index, Eigen::Index>>
32
+ conflicting_bound_indices;
33
+ };
34
+
35
+ /**
36
+ * A "bound constraint" is any linear constraint in one scalar variable.
37
+ *
38
+ * Computes which constraints, if any, are bound constraints, the tightest
39
+ * bounds on each decision variable, and whether or not they're feasible (given
40
+ * previously encountered bounds),
41
+ *
42
+ * @param decision_variables Decision variables corresponding to each column of
43
+ * A_i.
44
+ * @param inequality_constraints Variables representing the left-hand side of
45
+ * cᵢ(decision_variables) ≥ 0.
46
+ * @param A_i The Jacobian of inequality_constraints wrt decision_variables,
47
+ * evaluated anywhere, in *row-major* storage; in practice, since we typically
48
+ * store Jacobians column-major, the user of this function must perform a
49
+ * transpose.
50
+ */
51
+ inline Bounds get_bounds(
52
+ std::span<Variable> decision_variables,
53
+ std::span<Variable> inequality_constraints,
54
+ const Eigen::SparseMatrix<double, Eigen::RowMajor>& A_i) {
55
+ // TODO: A blocked, out-of-place transpose should be much faster than
56
+ // traversing row major on a column major matrix unless we have few linear
57
+ // constraints (using a heuristic to choose between this and staying column
58
+ // major based on the number of constraints would be an easy performance
59
+ // improvement.)
60
+
61
+ // NB: Casting to long is unspecified if the size of decision_variable.size()
62
+ // is greater than the max long value, but then we wouldn't be able to fill
63
+ // A_i correctly anyway.
64
+ slp_assert(static_cast<Eigen::Index>(decision_variables.size()) ==
65
+ A_i.innerSize());
66
+ slp_assert(static_cast<Eigen::Index>(inequality_constraints.size()) ==
67
+ A_i.outerSize());
68
+
69
+ // Maps each decision variable's index to the indices of its upper and lower
70
+ // bounds if they exist, or NO_BOUND if they do not; used only for bookkeeping
71
+ // to compute conflicting bounds
72
+ constexpr Eigen::Index NO_BOUND = -1;
73
+ gch::small_vector<std::pair<Eigen::Index, Eigen::Index>>
74
+ decision_var_indices_to_constraint_indices{decision_variables.size(),
75
+ {NO_BOUND, NO_BOUND}};
76
+ // Lists pairs of indices of bound constraints in the inequality constraint
77
+ // list that conflict with each other
78
+ gch::small_vector<std::pair<Eigen::Index, Eigen::Index>>
79
+ conflicting_bound_indices;
80
+
81
+ // Maps each decision variable's index to its upper and lower bounds
82
+ gch::small_vector<std::pair<double, double>> decision_var_indices_to_bounds{
83
+ decision_variables.size(),
84
+ {-std::numeric_limits<double>::infinity(),
85
+ std::numeric_limits<double>::infinity()}};
86
+
87
+ // Vector corresponding to the inequality constraints where the i-th element
88
+ // is 1 if the i-th inequality constraint is a bound and 0 otherwise.
89
+ Eigen::ArrayX<bool> bound_constraint_mask{inequality_constraints.size()};
90
+ bound_constraint_mask.fill(false);
91
+
92
+ for (decltype(inequality_constraints)::size_type constraint_index = 0;
93
+ constraint_index < inequality_constraints.size(); ++constraint_index) {
94
+ // A constraint is a bound iff it is linear and its gradient has a
95
+ // single nonzero value.
96
+ if (inequality_constraints[constraint_index].type() !=
97
+ ExpressionType::LINEAR) {
98
+ continue;
99
+ }
100
+ const Eigen::SparseVector<double>& row_A_i =
101
+ A_i.innerVector(constraint_index);
102
+ const auto non_zeros = row_A_i.nonZeros();
103
+ slp_assert(non_zeros != 0);
104
+ if (non_zeros > 1) {
105
+ // Constraint is in more than one variable and therefore not a bound.
106
+ continue;
107
+ }
108
+
109
+ // Claim: The bound given by a bound constraint is the constraint evaluated
110
+ // at zero divided by the nonzero element of the constraint's gradient.
111
+ //
112
+ // Proof: If c(x) is a bound constraint, then by definition c is a linear
113
+ // function in one variable, hence there exist a, b ∈ ℝ s.t. c(x) = axᵢ + b
114
+ // and a ≠ 0. The gradient of c is then aeᵢ (where eᵢ denotes the i-th basis
115
+ // element), and c(0) = b. If c(x) ≥ 0, then since either a < 0 or a > 0, we
116
+ // have either x ≤ -b/a or x ≥ -b/a, respectively. ∎
117
+ Eigen::SparseVector<double>::InnerIterator row_iter(row_A_i);
118
+ const auto constraint_coefficient =
119
+ row_iter
120
+ .value(); // The nonzero value of the j-th constraint's gradient.
121
+ const auto decision_variable_index = row_iter.index();
122
+ const auto decision_variable_value =
123
+ decision_variables[decision_variable_index].value();
124
+ double constraint_constant;
125
+ // We need to evaluate this constraint *exactly* at zero.
126
+ if (decision_variable_value != 0.0) {
127
+ decision_variables[decision_variable_index].set_value(0.0);
128
+ constraint_constant = inequality_constraints[constraint_index].value();
129
+ decision_variables[decision_variable_index].set_value(
130
+ decision_variable_value);
131
+ } else {
132
+ constraint_constant = inequality_constraints[constraint_index].value();
133
+ }
134
+
135
+ // Shouldn't happen since the constraint is supposed to be linear and not a
136
+ // constant.
137
+ slp_assert(constraint_coefficient != 0.0);
138
+
139
+ // We should always get a finite value when evaluating the constraint at
140
+ // x = 0 since the constraint is linear.
141
+ slp_assert(std::isfinite(constraint_constant));
142
+
143
+ // This is possible if the user has provided a starting point at which their
144
+ // problem is ill-defined.
145
+ slp_assert(std::isfinite(constraint_coefficient));
146
+
147
+ // Update bounds; we assume constraints of the form c(x) ≥ 0.
148
+ auto& [lower_bound, upper_bound] =
149
+ decision_var_indices_to_bounds[decision_variable_index];
150
+ auto& [lower_index, upper_index] =
151
+ decision_var_indices_to_constraint_indices[decision_variable_index];
152
+ const auto detected_bound = -constraint_constant / constraint_coefficient;
153
+ if (constraint_coefficient < 0.0 && detected_bound < upper_bound) {
154
+ upper_bound = detected_bound;
155
+ upper_index = constraint_index;
156
+ } else if (constraint_coefficient > 0.0 && detected_bound > lower_bound) {
157
+ lower_bound = detected_bound;
158
+ lower_index = constraint_index;
159
+ }
160
+
161
+ // Update conflicting bounds
162
+ if (lower_bound > upper_bound) {
163
+ conflicting_bound_indices.emplace_back(lower_index, upper_index);
164
+ }
165
+
166
+ // Set the bound constraint mask appropriately.
167
+ bound_constraint_mask[constraint_index] = true;
168
+ }
169
+ return {bound_constraint_mask, decision_var_indices_to_bounds,
170
+ conflicting_bound_indices};
171
+ }
172
+
173
+ /**
174
+ * Projects the decision variables onto the given bounds, while ensuring some
175
+ * configurable distance from the boundary if possible. This is designed to
176
+ * match the algorithm given in section 3.6 of [2].
177
+ *
178
+ * @param x A vector of decision variables.
179
+ * @param decision_variable_indices_to_bounds An array of bounds (stored [lower,
180
+ * upper]) for each decision variable in x.
181
+ * @param κ_1 A constant controlling distance from the lower or upper bound when
182
+ * the difference between the upper and lower bound is small.
183
+ * @param κ_2 A constant controlling distance from the lower or upper bound when
184
+ * the difference between the upper and lower bound is large (including when
185
+ * one of the bounds is ±∞).
186
+ */
187
+ template <typename Derived>
188
+ requires(static_cast<bool>(Eigen::DenseBase<Derived>::IsVectorAtCompileTime))
189
+ inline void project_onto_bounds(
190
+ Eigen::DenseBase<Derived>& x,
191
+ std::span<const std::pair<typename Eigen::DenseBase<Derived>::Scalar,
192
+ typename Eigen::DenseBase<Derived>::Scalar>>
193
+ decision_variable_indices_to_bounds,
194
+ const typename Eigen::DenseBase<Derived>::Scalar κ_1 = 1e-2,
195
+ const typename Eigen::DenseBase<Derived>::Scalar κ_2 = 1e-2) {
196
+ slp_assert(κ_1 > 0 && κ_2 > 0 && κ_2 < 0.5);
197
+
198
+ Eigen::Index decision_variable_idx = 0;
199
+ for (const auto& [lower, upper] : decision_variable_indices_to_bounds) {
200
+ typename Eigen::DenseBase<Derived>::Scalar& x_i =
201
+ x[decision_variable_idx++];
202
+
203
+ // We assume that bound infeasibility is handled elsewhere.
204
+ slp_assert(lower <= upper);
205
+
206
+ // See B.2 in [5] and section 3.6 in [2]
207
+ if (std::isfinite(lower) && std::isfinite(upper)) {
208
+ auto p_L =
209
+ std::min(κ_1 * std::max(1.0, std::abs(lower)), κ_2 * (upper - lower));
210
+ auto p_U =
211
+ std::min(κ_1 * std::max(1.0, std::abs(upper)), κ_2 * (upper - lower));
212
+ x_i = std::min(std::max(lower + p_L, x_i), upper - p_U);
213
+ } else if (std::isfinite(lower)) {
214
+ x_i = std::max(x_i, lower + κ_1 * std::max(1.0, std::abs(lower)));
215
+ } else if (std::isfinite(upper)) {
216
+ x_i = std::min(x_i, upper - κ_1 * std::max(1.0, std::abs(upper)));
217
+ }
218
+ }
219
+ }
220
+ } // namespace slp
@@ -13,6 +13,7 @@
13
13
  #include <Eigen/SparseCore>
14
14
  #include <gch/small_vector.hpp>
15
15
 
16
+ #include "optimization/bounds.hpp"
16
17
  #include "sleipnir/autodiff/expression_type.hpp"
17
18
  #include "sleipnir/autodiff/gradient.hpp"
18
19
  #include "sleipnir/autodiff/hessian.hpp"
@@ -252,6 +253,19 @@ ExitStatus Problem::solve(const Options& options, [[maybe_unused]] bool spy) {
252
253
  }
253
254
  #endif
254
255
 
256
+ const auto [bound_constraint_mask, bounds, conflicting_bound_indices] =
257
+ get_bounds(m_decision_variables, m_inequality_constraints, A_i.value());
258
+ if (!conflicting_bound_indices.empty()) {
259
+ if (options.diagnostics) {
260
+ print_bound_constraint_global_infeasibility_error(
261
+ conflicting_bound_indices);
262
+ }
263
+ return ExitStatus::GLOBALLY_INFEASIBLE;
264
+ }
265
+
266
+ #ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
267
+ project_onto_bounds(x, bounds);
268
+ #endif
255
269
  // Invoke interior-point method solver
256
270
  status = interior_point(
257
271
  InteriorPointMatrixCallbacks{
@@ -286,7 +300,11 @@ ExitStatus Problem::solve(const Options& options, [[maybe_unused]] bool spy) {
286
300
  x_ad.set_value(x);
287
301
  return A_i.value();
288
302
  }},
289
- m_iteration_callbacks, options, x);
303
+ m_iteration_callbacks, options,
304
+ #ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
305
+ bound_constraint_mask,
306
+ #endif
307
+ x);
290
308
  }
291
309
 
292
310
  #ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
@@ -56,7 +56,11 @@ ExitStatus interior_point(
56
56
  const InteriorPointMatrixCallbacks& matrix_callbacks,
57
57
  std::span<std::function<bool(const IterationInfo& info)>>
58
58
  iteration_callbacks,
59
- const Options& options, Eigen::VectorXd& x) {
59
+ const Options& options,
60
+ #ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
61
+ const Eigen::ArrayX<bool>& bound_constraint_mask,
62
+ #endif
63
+ Eigen::VectorXd& x) {
60
64
  const auto solve_start_time = std::chrono::steady_clock::now();
61
65
 
62
66
  gch::small_vector<SolveProfiler> solve_profilers;
@@ -160,6 +164,10 @@ ExitStatus interior_point(
160
164
  Eigen::SparseMatrix<double> A_i = matrices.A_i(x);
161
165
 
162
166
  Eigen::VectorXd s = Eigen::VectorXd::Ones(num_inequality_constraints);
167
+ #ifdef SLEIPNIR_ENABLE_BOUND_PROJECTION
168
+ // We set sʲ = cᵢʲ(x) for each bound inequality constraint index j
169
+ s = bound_constraint_mask.select(c_i, s);
170
+ #endif
163
171
  Eigen::VectorXd y = Eigen::VectorXd::Zero(num_equality_constraints);
164
172
  Eigen::VectorXd z = Eigen::VectorXd::Ones(num_inequality_constraints);
165
173
 
@@ -149,6 +149,25 @@ inline void print_c_i_local_infeasibility_error(const Eigen::VectorXd& c_i) {
149
149
  #define print_c_i_local_infeasibility_error(...)
150
150
  #endif
151
151
 
152
+ #ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
153
+ inline void print_bound_constraint_global_infeasibility_error(
154
+ const std::span<const std::pair<Eigen::Index, Eigen::Index>>
155
+ conflicting_lower_upper_bound_indices) {
156
+ slp::println(
157
+ "The problem is globally infeasible due to conflicting bound "
158
+ "constraints:");
159
+ for (const auto& [lower_bound_idx, upper_bound_idx] :
160
+ conflicting_lower_upper_bound_indices) {
161
+ slp::println(
162
+ " Inequality constraint {} gives a lower bound that is greater than "
163
+ "the upper bound given by inequality constraint {}",
164
+ lower_bound_idx, upper_bound_idx);
165
+ }
166
+ }
167
+ #else
168
+ #define print_bound_constraint_global_infeasibility_error(...)
169
+ #endif
170
+
152
171
  #ifndef SLEIPNIR_DISABLE_DIAGNOSTICS
153
172
  /**
154
173
  * Prints diagnostics for the current iteration.