sleipnirgroup-jormungandr 0.0.1.dev475__tar.gz → 0.0.1.dev476__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.dev476}/CMakeLists.txt +9 -0
  2. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/PKG-INFO +1 -1
  3. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/optimization/solver/exit_status.hpp +11 -6
  4. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/optimization/solver/interior_point.hpp +5 -1
  5. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/docstrings.hpp +4 -0
  6. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/optimization/solver/bind_exit_status.cpp +2 -0
  7. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/nonlinear_problem_test.py +19 -0
  8. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/pyproject.toml +1 -1
  9. sleipnirgroup_jormungandr-0.0.1.dev476/src/optimization/bounds.hpp +220 -0
  10. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/problem.cpp +19 -1
  11. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/solver/interior_point.cpp +9 -1
  12. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/util/print_diagnostics.hpp +19 -0
  13. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/LICENSE.txt +0 -0
  14. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/README.md +0 -0
  15. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/SleipnirConfig.cmake.in +0 -0
  16. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/cmake/modules/BuildTypes.cmake +0 -0
  17. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/cmake/modules/CompilerFlags.cmake +0 -0
  18. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/cmake/modules/Pybind11Mkdoc.cmake +0 -0
  19. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/cmake/modules/SubdirList.cmake +0 -0
  20. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/.styleguide +0 -0
  21. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/adjoint_expression_graph.hpp +0 -0
  22. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/expression.hpp +0 -0
  23. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/expression_graph.hpp +0 -0
  24. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/expression_type.hpp +0 -0
  25. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/gradient.hpp +0 -0
  26. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/hessian.hpp +0 -0
  27. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/jacobian.hpp +0 -0
  28. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/slice.hpp +0 -0
  29. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/variable.hpp +0 -0
  30. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/variable_block.hpp +0 -0
  31. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/autodiff/variable_matrix.hpp +0 -0
  32. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/control/ocp.hpp +0 -0
  33. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/optimization/multistart.hpp +0 -0
  34. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/optimization/problem.hpp +0 -0
  35. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/optimization/solver/iteration_info.hpp +0 -0
  36. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/optimization/solver/newton.hpp +0 -0
  37. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/optimization/solver/options.hpp +0 -0
  38. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/optimization/solver/sqp.hpp +0 -0
  39. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/util/assert.hpp +0 -0
  40. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/util/concepts.hpp +0 -0
  41. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/util/function_ref.hpp +0 -0
  42. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/util/intrusive_shared_ptr.hpp +0 -0
  43. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/util/pool.hpp +0 -0
  44. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/util/print.hpp +0 -0
  45. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/util/spy.hpp +0 -0
  46. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/include/sleipnir/util/symbol_exports.hpp +0 -0
  47. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/__init__.py +0 -0
  48. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/autodiff/__init__.py +0 -0
  49. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/control/__init__.py +0 -0
  50. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/autodiff/bind_expression_type.cpp +0 -0
  51. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/autodiff/bind_gradient.cpp +0 -0
  52. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/autodiff/bind_hessian.cpp +0 -0
  53. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/autodiff/bind_jacobian.cpp +0 -0
  54. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/autodiff/bind_variable.cpp +0 -0
  55. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/autodiff/bind_variable_block.cpp +0 -0
  56. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/autodiff/bind_variable_matrix.cpp +0 -0
  57. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/binders.hpp +0 -0
  58. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/control/bind_ocp.cpp +0 -0
  59. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/main.cpp +0 -0
  60. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/optimization/bind_equality_constraints.cpp +0 -0
  61. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/optimization/bind_inequality_constraints.cpp +0 -0
  62. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/optimization/bind_problem.cpp +0 -0
  63. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/optimization/solver/bind_iteration_info.cpp +0 -0
  64. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/cpp/try_cast.hpp +0 -0
  65. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/optimization/__init__.py +0 -0
  66. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/autodiff/gradient_test.py +0 -0
  67. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/autodiff/hessian_test.py +0 -0
  68. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/autodiff/jacobian_test.py +0 -0
  69. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/autodiff/variable_matrix_test.py +0 -0
  70. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/autodiff/variable_test.py +0 -0
  71. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/cart_pole_util.py +0 -0
  72. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/control/cart_pole_ocp_test.py +0 -0
  73. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/control/differential_drive_ocp_test.py +0 -0
  74. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/control/flywheel_ocp_test.py +0 -0
  75. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/differential_drive_util.py +0 -0
  76. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/arm_on_elevator_problem_test.py +0 -0
  77. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/cart_pole_problem_test.py +0 -0
  78. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/constraints_test.py +0 -0
  79. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/decision_variable_test.py +0 -0
  80. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/differential_drive_problem_test.py +0 -0
  81. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/double_integrator_problem_test.py +0 -0
  82. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/exit_status_test.py +0 -0
  83. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/flywheel_problem_test.py +0 -0
  84. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/linear_problem_test.py +0 -0
  85. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/multistart_test.py +0 -0
  86. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/quadratic_problem_test.py +0 -0
  87. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/optimization/trivial_problem_test.py +0 -0
  88. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/jormungandr/test/rk4.py +0 -0
  89. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/.styleguide +0 -0
  90. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/autodiff/variable_matrix.cpp +0 -0
  91. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/inertia.hpp +0 -0
  92. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/regularized_ldlt.hpp +0 -0
  93. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/solver/newton.cpp +0 -0
  94. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/solver/sqp.cpp +0 -0
  95. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/solver/util/error_estimate.hpp +0 -0
  96. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/solver/util/filter.hpp +0 -0
  97. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/solver/util/fraction_to_the_boundary_rule.hpp +0 -0
  98. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/solver/util/is_locally_infeasible.hpp +0 -0
  99. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/optimization/solver/util/kkt_error.hpp +0 -0
  100. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/util/pool.cpp +0 -0
  101. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/util/scope_exit.hpp +0 -0
  102. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/util/scoped_profiler.hpp +0 -0
  103. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/src/util/setup_profiler.hpp +0 -0
  104. {sleipnirgroup_jormungandr-0.0.1.dev475 → sleipnirgroup_jormungandr-0.0.1.dev476}/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.dev476
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
 
@@ -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.dev476"
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.