sleipnirgroup-jormungandr 0.6.7.dev54__tar.gz → 0.6.7.dev58__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.

Potentially problematic release.


This version of sleipnirgroup-jormungandr might be problematic. Click here for more details.

Files changed (126) hide show
  1. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/CMakeLists.txt +36 -16
  2. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/PKG-INFO +1 -1
  3. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/pyproject.toml +22 -0
  4. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_variable_block.cpp +51 -23
  5. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_variable_matrix.cpp +59 -52
  6. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/bind_ocp.cpp +17 -0
  7. sleipnirgroup_jormungandr-0.6.7.dev58/python/cpp/slice_tuple.hpp +21 -0
  8. sleipnirgroup_jormungandr-0.6.7.dev58/python/postprocess_stubs.py +166 -0
  9. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/src/sleipnir/__init__.py +1 -1
  10. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/src/sleipnir/autodiff/__init__.py +1 -3
  11. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/src/sleipnir/optimization/__init__.py +5 -1
  12. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/gradient_test.py +20 -20
  13. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/hessian_test.py +31 -30
  14. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/jacobian_test.py +11 -11
  15. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/variable_matrix_test.py +21 -21
  16. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/variable_test.py +3 -3
  17. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/arm_on_elevator_problem_test.py +1 -1
  18. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/cart_pole_ocp_test.py +3 -3
  19. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/cart_pole_problem_test.py +2 -2
  20. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/cart_pole_util.py +8 -3
  21. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/constraints_test.py +4 -4
  22. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/decision_variable_test.py +4 -4
  23. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/differential_drive_ocp_test.py +1 -1
  24. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/differential_drive_problem_test.py +2 -2
  25. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/differential_drive_util.py +8 -3
  26. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/double_integrator_problem_test.py +1 -1
  27. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/flywheel_ocp_test.py +5 -5
  28. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/flywheel_problem_test.py +1 -1
  29. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/linear_problem_test.py +2 -2
  30. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/multistart_test.py +5 -3
  31. sleipnirgroup_jormungandr-0.6.7.dev58/python/test/optimization/near.py +2 -0
  32. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/nonlinear_problem_test.py +7 -7
  33. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/problem_spy_test.py +7 -5
  34. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/quadratic_problem_test.py +6 -6
  35. sleipnirgroup_jormungandr-0.6.7.dev58/python/test/optimization/rk4.py +44 -0
  36. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/solver/exit_status_test.py +15 -15
  37. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/trivial_problem_test.py +3 -3
  38. sleipnirgroup_jormungandr-0.6.7.dev54/python/test/optimization/near.py +0 -2
  39. sleipnirgroup_jormungandr-0.6.7.dev54/python/test/optimization/rk4.py +0 -18
  40. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/LICENSE.txt +0 -0
  41. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/README.md +0 -0
  42. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/SleipnirConfig.cmake.in +0 -0
  43. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/BuildTypes.cmake +0 -0
  44. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/CompilerFlags.cmake +0 -0
  45. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/Pybind11Mkdoc.cmake +0 -0
  46. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/SubdirList.cmake +0 -0
  47. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/arm-none-eabi.cmake +0 -0
  48. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/expression.hpp +0 -0
  49. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/expression_graph.hpp +0 -0
  50. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/expression_type.hpp +0 -0
  51. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/gradient.hpp +0 -0
  52. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/hessian.hpp +0 -0
  53. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/jacobian.hpp +0 -0
  54. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/sleipnir_base.hpp +0 -0
  55. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/slice.hpp +0 -0
  56. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/variable.hpp +0 -0
  57. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/variable_block.hpp +0 -0
  58. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/variable_matrix.hpp +0 -0
  59. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/multistart.hpp +0 -0
  60. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/ocp/dynamics_type.hpp +0 -0
  61. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/ocp/timestep_method.hpp +0 -0
  62. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/ocp/transcription_method.hpp +0 -0
  63. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/ocp.hpp +0 -0
  64. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/problem.hpp +0 -0
  65. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/exit_status.hpp +0 -0
  66. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/ipm.hpp +0 -0
  67. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/ipm_matrix_callbacks.hpp +0 -0
  68. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/iteration_info.hpp +0 -0
  69. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/newton.hpp +0 -0
  70. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/newton_matrix_callbacks.hpp +0 -0
  71. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/options.hpp +0 -0
  72. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/sqp.hpp +0 -0
  73. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/sqp_matrix_callbacks.hpp +0 -0
  74. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/all_finite.hpp +0 -0
  75. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/append_as_triplets.hpp +0 -0
  76. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/bounds.hpp +0 -0
  77. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/dense_kkt_solver.hpp +0 -0
  78. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/feasibility_restoration.hpp +0 -0
  79. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/filter.hpp +0 -0
  80. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/fraction_to_the_boundary_rule.hpp +0 -0
  81. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/inertia.hpp +0 -0
  82. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/kkt_error.hpp +0 -0
  83. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/kkt_solver.hpp +0 -0
  84. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/problem_scaling.hpp +0 -0
  85. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/sparse_inf_norms.hpp +0 -0
  86. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/sparse_kkt_solver.hpp +0 -0
  87. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/assert.hpp +0 -0
  88. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/concepts.hpp +0 -0
  89. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/empty.hpp +0 -0
  90. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/function_ref.hpp +0 -0
  91. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/intrusive_shared_ptr.hpp +0 -0
  92. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/pool.hpp +0 -0
  93. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/print.hpp +0 -0
  94. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/print_diagnostics.hpp +0 -0
  95. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/profiler.hpp +0 -0
  96. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/scope_exit.hpp +0 -0
  97. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/spy.hpp +0 -0
  98. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/symbol_exports.hpp +0 -0
  99. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_expression_type.cpp +0 -0
  100. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_gradient.cpp +0 -0
  101. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_hessian.cpp +0 -0
  102. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_jacobian.cpp +0 -0
  103. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_variable.cpp +0 -0
  104. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/binders.hpp +0 -0
  105. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/docstrings.hpp +0 -0
  106. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/for_each_type.hpp +0 -0
  107. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/main.cpp +0 -0
  108. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/bind_equality_constraints.cpp +0 -0
  109. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/bind_inequality_constraints.cpp +0 -0
  110. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/bind_problem.cpp +0 -0
  111. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/ocp/bind_dynamics_type.cpp +0 -0
  112. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/ocp/bind_timestep_method.cpp +0 -0
  113. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/ocp/bind_transcription_method.cpp +0 -0
  114. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/solver/bind_exit_status.cpp +0 -0
  115. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/solver/bind_iteration_info.cpp +0 -0
  116. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/try_cast.hpp +0 -0
  117. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/autodiff/gradient.cpp +0 -0
  118. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/autodiff/hessian.cpp +0 -0
  119. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/autodiff/jacobian.cpp +0 -0
  120. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/autodiff/variable_matrix.cpp +0 -0
  121. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/ocp.cpp +0 -0
  122. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/problem.cpp +0 -0
  123. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/solver/ipm.cpp +0 -0
  124. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/solver/newton.cpp +0 -0
  125. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/solver/sqp.cpp +0 -0
  126. {sleipnirgroup_jormungandr-0.6.7.dev54 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/util/pool.cpp +0 -0
@@ -158,8 +158,7 @@ if(SLEIPNIR_BUILD_BENCHMARKS)
158
158
  foreach(benchmark "cart_pole" "flywheel")
159
159
  file(
160
160
  GLOB ${benchmark}_optimization_benchmark_src
161
- benchmarks/optimization/*.cpp
162
- benchmarks/optimization/${benchmark}/*.cpp
161
+ benchmarks/${benchmark}_optimization/*.cpp
163
162
  )
164
163
  add_executable(
165
164
  ${benchmark}_optimization_benchmark
@@ -168,9 +167,7 @@ if(SLEIPNIR_BUILD_BENCHMARKS)
168
167
  compiler_flags(${benchmark}_optimization_benchmark)
169
168
  target_include_directories(
170
169
  ${benchmark}_optimization_benchmark
171
- PRIVATE
172
- ${CMAKE_CURRENT_SOURCE_DIR}/benchmarks
173
- ${CMAKE_CURRENT_SOURCE_DIR}/benchmarks/optimization
170
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/benchmarks
174
171
  )
175
172
  target_link_libraries(
176
173
  ${benchmark}_optimization_benchmark
@@ -304,7 +301,9 @@ if(SLEIPNIR_BUILD_PYTHON)
304
301
  if(DEFINED PY_BUILD_CMAKE_MODULE_NAME)
305
302
  set(PY_DEST ${PY_BUILD_CMAKE_MODULE_NAME})
306
303
  else()
307
- set(PY_DEST lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR})
304
+ set(PY_DEST
305
+ lib/python${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/sleipnir
306
+ )
308
307
  endif()
309
308
 
310
309
  # nanobind dependency
@@ -346,34 +345,55 @@ if(SLEIPNIR_BUILD_PYTHON)
346
345
  LIBRARY DESTINATION ${PY_DEST}
347
346
  )
348
347
 
348
+ # Generate stubs at install time because only the install tree has the
349
+ # sleipnir/ package layout needed to import the module by its fully
350
+ # qualified name `sleipnir._sleipnir`.
351
+ #
352
+ # Importing sleipnir._sleipnir requires the directory containing the
353
+ # sleipnir package (i.e., PY_DEST's parent) to be on the Python path. For
354
+ # py-build-cmake builds, PY_DEST has no parent. For plain CMake installs,
355
+ # PY_DEST's parent is lib/pythonX.Y.
356
+ cmake_path(GET PY_DEST PARENT_PATH PY_DEST_PARENT)
349
357
  nanobind_add_stub(
350
358
  _sleipnir_stub
351
359
  INSTALL_TIME
352
- MARKER_FILE sleipnir/py.typed
353
- MODULE _sleipnir
354
- OUTPUT sleipnir/__init__.pyi
355
- PYTHON_PATH $<TARGET_FILE_DIR:_sleipnir>
360
+ MARKER_FILE ${PY_DEST}/py.typed
361
+ MODULE sleipnir._sleipnir
362
+ OUTPUT ${PY_DEST}/_sleipnir/__init__.pyi
363
+ PYTHON_PATH ${PY_DEST_PARENT}
356
364
  DEPENDS _sleipnir
357
365
  COMPONENT python_modules
358
366
  )
359
367
  nanobind_add_stub(
360
368
  _sleipnir_autodiff_stub
361
369
  INSTALL_TIME
362
- MODULE _sleipnir.autodiff
363
- OUTPUT sleipnir/autodiff/__init__.pyi
364
- PYTHON_PATH $<TARGET_FILE_DIR:_sleipnir>
370
+ MODULE sleipnir._sleipnir.autodiff
371
+ OUTPUT ${PY_DEST}/_sleipnir/autodiff.pyi
372
+ PYTHON_PATH ${PY_DEST_PARENT}
365
373
  DEPENDS _sleipnir
366
374
  COMPONENT python_modules
367
375
  )
368
376
  nanobind_add_stub(
369
377
  _sleipnir_optimization_stub
370
378
  INSTALL_TIME
371
- MODULE _sleipnir.optimization
372
- OUTPUT sleipnir/optimization/__init__.pyi
373
- PYTHON_PATH $<TARGET_FILE_DIR:_sleipnir>
379
+ MODULE sleipnir._sleipnir.optimization
380
+ OUTPUT ${PY_DEST}/_sleipnir/optimization.pyi
381
+ PYTHON_PATH ${PY_DEST_PARENT}
374
382
  DEPENDS _sleipnir
375
383
  COMPONENT python_modules
376
384
  )
385
+ install(
386
+ CODE
387
+ "execute_process(
388
+ COMMAND \"${Python_EXECUTABLE}\"
389
+ \"${CMAKE_CURRENT_SOURCE_DIR}/python/postprocess_stubs.py\"
390
+ ${PY_DEST}/_sleipnir/autodiff.pyi
391
+ ${PY_DEST}/_sleipnir/optimization.pyi
392
+ WORKING_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}\"
393
+ COMMAND_ERROR_IS_FATAL ANY
394
+ )"
395
+ COMPONENT python_modules
396
+ )
377
397
 
378
398
  if(NOT CMAKE_CROSSCOMPILING)
379
399
  # pybind11_mkdoc dependency
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sleipnirgroup-jormungandr
3
- Version: 0.6.7.dev54
3
+ Version: 0.6.7.dev58
4
4
  Summary: Reverse mode autodiff library and NLP solver DSL
5
5
  License-Expression: BSD-3-Clause
6
6
  License-File: LICENSE.txt
@@ -21,7 +21,9 @@ build-backend = "py_build_cmake.build"
21
21
 
22
22
  [dependency-groups]
23
23
  dev = [
24
+ "mypy>=2.4.0",
24
25
  "pytest>=9.1.1",
26
+ "scipy-stubs",
25
27
  ]
26
28
 
27
29
  [tool.py-build-cmake.cmake]
@@ -54,8 +56,27 @@ exclude = []
54
56
  python_abi = "abi3"
55
57
  abi3_minimum_cpython_version = 312
56
58
 
59
+ [tool.mypy]
60
+ files = [
61
+ "benchmarks",
62
+ "docs/generate_website.py",
63
+ "examples",
64
+ "python/postprocess_stubs.py",
65
+ "python/test",
66
+ "tools",
67
+ ]
68
+ allow_redefinition_new = true
69
+ explicit_package_bases = true
70
+ local_partial_types = true
71
+ mypy_path = ["examples/current_manager", "python/test/optimization"]
72
+
73
+ [[tool.mypy.overrides]]
74
+ module = "mpl_toolkits.*"
75
+ ignore_missing_imports = true
76
+
57
77
  [tool.pytest.ini_options]
58
78
  minversion = "6.0"
79
+ pythonpath = ["examples/current_manager"]
59
80
  testpaths = ["python/test", "examples/current_manager/test"]
60
81
 
61
82
  [tool.uv]
@@ -68,6 +89,7 @@ cache-keys = [
68
89
  { file = "python/cpp/**/*.cpp" },
69
90
  { file = "python/cpp/binders.hpp" },
70
91
  { file = "python/cpp/for_each_type.hpp" },
92
+ { file = "python/cpp/slice_tuple.hpp" },
71
93
  { file = "python/cpp/try_cast.hpp" },
72
94
  { file = "python/src/**" },
73
95
  { file = "src/**" },
@@ -6,6 +6,7 @@
6
6
  #include <nanobind/operators.h>
7
7
  #include <nanobind/stl/function.h>
8
8
  #include <nanobind/stl/string.h>
9
+ #include <nanobind/stl/tuple.h>
9
10
  #include <nanobind/stl/vector.h>
10
11
 
11
12
  #include <stdint.h>
@@ -15,6 +16,7 @@
15
16
  #include <functional>
16
17
  #include <stdexcept>
17
18
  #include <string>
19
+ #include <tuple>
18
20
 
19
21
  #include <Eigen/Core>
20
22
  #include <sleipnir/autodiff/slice.hpp>
@@ -25,6 +27,7 @@
25
27
 
26
28
  #include "docstrings.hpp"
27
29
  #include "for_each_type.hpp"
30
+ #include "slice_tuple.hpp"
28
31
  #include "try_cast.hpp"
29
32
 
30
33
  namespace nb = nanobind;
@@ -68,7 +71,7 @@ void bind_variable_block(
68
71
  "row"_a, "value"_a);
69
72
  cls.def(
70
73
  "__setitem__",
71
- [](VariableBlock<VariableMatrix<double>>& self, nb::tuple slices,
74
+ [](VariableBlock<VariableMatrix<double>>& self, SliceTuple slices,
72
75
  nb::object value) {
73
76
  if (slices.size() != 2) {
74
77
  throw nb::index_error(
@@ -151,31 +154,32 @@ void bind_variable_block(
151
154
  cls.def(
152
155
  "__getitem__",
153
156
  [](VariableBlock<VariableMatrix<double>>& self,
154
- nb::tuple slices) -> nb::object {
157
+ std::tuple<int, int> slices) -> Variable<double>& {
158
+ auto [row, col] = slices;
159
+
160
+ if (row >= self.rows() || col >= self.cols()) {
161
+ throw std::out_of_range("Index out of bounds");
162
+ }
163
+
164
+ if (row < 0) {
165
+ row += self.rows();
166
+ }
167
+ if (col < 0) {
168
+ col += self.cols();
169
+ }
170
+ return self[row, col];
171
+ },
172
+ nb::rv_policy::reference_internal, "slices"_a,
173
+ DOC(slp, VariableBlock, operator, array));
174
+ cls.def(
175
+ "__getitem__",
176
+ [](VariableBlock<VariableMatrix<double>>& self,
177
+ SliceTuple slices) -> VariableBlock<VariableMatrix<double>> {
155
178
  if (slices.size() != 2) {
156
179
  throw nb::index_error(
157
180
  std::format("Expected 2 slices, got {}.", slices.size()).c_str());
158
181
  }
159
182
 
160
- // If both indices are integers instead of slices, return Variable
161
- // instead of VariableBlock
162
- if (nb::isinstance<int>(slices[0]) && nb::isinstance<int>(slices[1])) {
163
- int row = nb::cast<int>(slices[0]);
164
- int col = nb::cast<int>(slices[1]);
165
-
166
- if (row >= self.rows() || col >= self.cols()) {
167
- throw std::out_of_range("Index out of bounds");
168
- }
169
-
170
- if (row < 0) {
171
- row += self.rows();
172
- }
173
- if (col < 0) {
174
- col += self.cols();
175
- }
176
- return nb::cast(self[row, col]);
177
- }
178
-
179
183
  Slice row_slice;
180
184
  int row_slice_length;
181
185
  Slice col_slice;
@@ -211,8 +215,7 @@ void bind_variable_block(
211
215
  col_slice_length = 1;
212
216
  }
213
217
 
214
- return nb::cast(
215
- self[row_slice, row_slice_length, col_slice, col_slice_length]);
218
+ return self[row_slice, row_slice_length, col_slice, col_slice_length];
216
219
  },
217
220
  nb::keep_alive<0, 1>(), DOC(slp, VariableBlock, operator, array),
218
221
  "slices"_a);
@@ -223,6 +226,31 @@ void bind_variable_block(
223
226
  nb::overload_cast<int>(&VariableBlock<VariableMatrix<double>>::col),
224
227
  "col"_a, DOC(slp, VariableBlock, col));
225
228
 
229
+ // Matrix-matrix multiplication. Products with a VariableMatrix on the left
230
+ // are bound on VariableMatrix.
231
+ for (const char* name : {"__mul__", "__matmul__"}) {
232
+ cls.def(
233
+ name,
234
+ [](const VariableBlock<VariableMatrix<double>>& lhs,
235
+ const VariableBlock<VariableMatrix<double>>& rhs) {
236
+ return lhs * rhs;
237
+ },
238
+ nb::is_operator(), "rhs"_a);
239
+ }
240
+
241
+ // Matrix multiplication with NumPy arrays. These are also handled by
242
+ // __array_ufunc__ below, but type checkers only see these overloads.
243
+ cls.def(
244
+ "__matmul__",
245
+ [](const VariableBlock<VariableMatrix<double>>& lhs,
246
+ nb::DRef<Eigen::MatrixXd> rhs) { return lhs * rhs; },
247
+ nb::is_operator(), "rhs"_a);
248
+ cls.def(
249
+ "__rmatmul__",
250
+ [](const VariableBlock<VariableMatrix<double>>& rhs,
251
+ nb::DRef<Eigen::MatrixXd> lhs) { return lhs * rhs; },
252
+ nb::is_operator(), "lhs"_a);
253
+
226
254
  // https://numpy.org/doc/stable/user/basics.dispatch.html
227
255
  cls.def(
228
256
  "__array_ufunc__",
@@ -6,6 +6,7 @@
6
6
  #include <nanobind/operators.h>
7
7
  #include <nanobind/stl/function.h>
8
8
  #include <nanobind/stl/string.h>
9
+ #include <nanobind/stl/tuple.h>
9
10
  #include <nanobind/stl/vector.h>
10
11
 
11
12
  #include <stdint.h>
@@ -15,6 +16,7 @@
15
16
  #include <functional>
16
17
  #include <stdexcept>
17
18
  #include <string>
19
+ #include <tuple>
18
20
  #include <vector>
19
21
 
20
22
  #include <Eigen/Core>
@@ -26,6 +28,7 @@
26
28
 
27
29
  #include "docstrings.hpp"
28
30
  #include "for_each_type.hpp"
31
+ #include "slice_tuple.hpp"
29
32
  #include "try_cast.hpp"
30
33
 
31
34
  namespace nb = nanobind;
@@ -90,7 +93,7 @@ void bind_variable_matrix(nb::module_& autodiff,
90
93
  "row"_a, "value"_a);
91
94
  cls.def(
92
95
  "__setitem__",
93
- [](VariableMatrix<double>& self, nb::tuple slices, nb::object value) {
96
+ [](VariableMatrix<double>& self, SliceTuple slices, nb::object value) {
94
97
  if (slices.size() != 2) {
95
98
  throw nb::index_error(
96
99
  std::format("Expected 2 slices, got {}.", slices.size()).c_str());
@@ -170,31 +173,33 @@ void bind_variable_matrix(nb::module_& autodiff,
170
173
  DOC(slp, VariableMatrix, operator, array, 3));
171
174
  cls.def(
172
175
  "__getitem__",
173
- [](VariableMatrix<double>& self, nb::tuple slices) -> nb::object {
176
+ [](VariableMatrix<double>& self,
177
+ std::tuple<int, int> slices) -> Variable<double>& {
178
+ auto [row, col] = slices;
179
+
180
+ if (row >= self.rows() || col >= self.cols()) {
181
+ throw std::out_of_range("Index out of bounds");
182
+ }
183
+
184
+ if (row < 0) {
185
+ row += self.rows();
186
+ }
187
+ if (col < 0) {
188
+ col += self.cols();
189
+ }
190
+ return self[row, col];
191
+ },
192
+ nb::rv_policy::reference_internal, "slices"_a,
193
+ DOC(slp, VariableMatrix, operator, array));
194
+ cls.def(
195
+ "__getitem__",
196
+ [](VariableMatrix<double>& self,
197
+ SliceTuple slices) -> VariableBlock<VariableMatrix<double>> {
174
198
  if (slices.size() != 2) {
175
199
  throw nb::index_error(
176
200
  std::format("Expected 2 slices, got {}.", slices.size()).c_str());
177
201
  }
178
202
 
179
- // If both indices are integers instead of slices, return Variable
180
- // instead of VariableBlock
181
- if (nb::isinstance<int>(slices[0]) && nb::isinstance<int>(slices[1])) {
182
- int row = nb::cast<int>(slices[0]);
183
- int col = nb::cast<int>(slices[1]);
184
-
185
- if (row >= self.rows() || col >= self.cols()) {
186
- throw std::out_of_range("Index out of bounds");
187
- }
188
-
189
- if (row < 0) {
190
- row += self.rows();
191
- }
192
- if (col < 0) {
193
- col += self.cols();
194
- }
195
- return nb::cast(self[row, col]);
196
- }
197
-
198
203
  Slice row_slice;
199
204
  int row_slice_length;
200
205
  Slice col_slice;
@@ -227,8 +232,7 @@ void bind_variable_matrix(nb::module_& autodiff,
227
232
  col_slice_length = 1;
228
233
  }
229
234
 
230
- return nb::cast(
231
- self[row_slice, row_slice_length, col_slice, col_slice_length]);
235
+ return self[row_slice, row_slice_length, col_slice, col_slice_length];
232
236
  },
233
237
  nb::keep_alive<0, 1>(), "slices"_a,
234
238
  DOC(slp, VariableMatrix, operator, array));
@@ -237,43 +241,46 @@ void bind_variable_matrix(nb::module_& autodiff,
237
241
  cls.def("col", nb::overload_cast<int>(&VariableMatrix<double>::col), "col"_a,
238
242
  DOC(slp, VariableMatrix, col));
239
243
 
240
- // Matrix-matrix multiplication
241
- cls.def(
242
- "__mul__",
243
- [](const VariableMatrix<double>& lhs, const VariableMatrix<double>& rhs) {
244
- return lhs * rhs;
245
- },
246
- nb::is_operator(), "rhs"_a);
244
+ // Matrix-matrix multiplication. Products with a VariableBlock on the left are
245
+ // bound on VariableBlock.
246
+ for (const char* name : {"__mul__", "__matmul__"}) {
247
+ cls.def(
248
+ name,
249
+ [](const VariableMatrix<double>& lhs,
250
+ const VariableMatrix<double>& rhs) { return lhs * rhs; },
251
+ nb::is_operator(), "rhs"_a);
252
+ cls.def(
253
+ name,
254
+ [](const VariableMatrix<double>& lhs,
255
+ const VariableBlock<VariableMatrix<double>>& rhs) {
256
+ return lhs * rhs;
257
+ },
258
+ nb::is_operator(), "rhs"_a);
259
+ }
260
+ for (const char* name : {"__rmul__", "__rmatmul__"}) {
261
+ cls.def(
262
+ name,
263
+ [](const VariableMatrix<double>& rhs,
264
+ const VariableBlock<VariableMatrix<double>>& lhs) {
265
+ return lhs * rhs;
266
+ },
267
+ nb::is_operator(), "lhs"_a);
268
+ }
269
+
270
+ // Matrix multiplication with NumPy arrays. These are also handled by
271
+ // __array_ufunc__ below, but type checkers only see these overloads.
247
272
  cls.def(
248
- "__mul__",
249
- [](const VariableMatrix<double>& lhs,
250
- const VariableBlock<VariableMatrix<double>>& rhs) {
273
+ "__matmul__",
274
+ [](const VariableMatrix<double>& lhs, nb::DRef<Eigen::MatrixXd> rhs) {
251
275
  return lhs * rhs;
252
276
  },
253
277
  nb::is_operator(), "rhs"_a);
254
278
  cls.def(
255
- "__rmul__",
256
- [](const VariableMatrix<double>& rhs,
257
- const VariableBlock<VariableMatrix<double>>& lhs) {
279
+ "__rmatmul__",
280
+ [](const VariableMatrix<double>& rhs, nb::DRef<Eigen::MatrixXd> lhs) {
258
281
  return lhs * rhs;
259
282
  },
260
283
  nb::is_operator(), "lhs"_a);
261
- cls.def(
262
- "__mul__",
263
- [](const VariableBlock<VariableMatrix<double>>& lhs,
264
- const VariableBlock<VariableMatrix<double>>& rhs) {
265
- return lhs * rhs;
266
- },
267
- nb::is_operator(), "rhs"_a);
268
- for_each_type<VariableMatrix<double>, VariableBlock<VariableMatrix<double>>>(
269
- [&]<typename L> {
270
- for_each_type<VariableMatrix<double>,
271
- VariableBlock<VariableMatrix<double>>>([&]<typename R> {
272
- cls.def(
273
- "__matmul__", [](L&& lhs, R&& rhs) { return lhs * rhs; },
274
- nb::is_operator(), "rhs"_a);
275
- });
276
- });
277
284
 
278
285
  // https://numpy.org/doc/stable/user/basics.dispatch.html
279
286
  cls.def(
@@ -21,6 +21,23 @@
21
21
 
22
22
  namespace nb = nanobind;
23
23
 
24
+ namespace nanobind::detail {
25
+
26
+ // nanobind's duration caster accepts a float in seconds as well as a
27
+ // timedelta, but its type annotation only advertises timedelta because float is
28
+ // an implicit conversion. Advertise float in the stub's input type so mypy
29
+ // accepts passing a float to the dt parameter of OCP's constructor,
30
+ // set_min_timestep(), and set_max_timestep().
31
+ template <>
32
+ class type_caster<std::chrono::duration<double>>
33
+ : public duration_caster<std::chrono::duration<double>> {
34
+ public:
35
+ static constexpr auto Name =
36
+ io_name("datetime.timedelta | float", "datetime.timedelta");
37
+ };
38
+
39
+ } // namespace nanobind::detail
40
+
24
41
  namespace slp {
25
42
 
26
43
  void bind_ocp(nb::class_<OCP<double>, Problem<double>>& cls) {
@@ -0,0 +1,21 @@
1
+ // Copyright (c) Sleipnir contributors
2
+
3
+ #pragma once
4
+
5
+ #include <nanobind/nanobind.h>
6
+ #include <nanobind/stl/variant.h>
7
+
8
+ #include <variant>
9
+
10
+ namespace nb = nanobind;
11
+
12
+ namespace slp {
13
+
14
+ /// A 2D index of the form (row, col) where each element is an int or a slice.
15
+ ///
16
+ /// The element types only affect the generated type stubs; the tuple's contents
17
+ /// are checked at runtime.
18
+ using SliceTuple = nb::typed<nb::tuple, std::variant<int, nb::slice>,
19
+ std::variant<int, nb::slice>>;
20
+
21
+ } // namespace slp
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env python
2
+
3
+ """
4
+ Fixes up type stubs generated by nanobind's stubgen.
5
+
6
+ stubgen can't express some things the bindings support at runtime:
7
+
8
+ 1. VariableBlock implicitly converts to VariableMatrix, so every VariableMatrix
9
+ parameter also accepts a VariableBlock. Return types and callback arguments
10
+ are left alone since those are always VariableMatrix.
11
+ 2. Sparse matrices are annotated as scipy.sparse.csc_matrix[float], but
12
+ scipy-stubs expects a NumPy numeric scalar type argument.
13
+ """
14
+
15
+ import argparse
16
+ import ast
17
+ import re
18
+ from pathlib import Path
19
+
20
+ PREFIX = r"(?:sleipnir\._sleipnir\.autodiff\.)?"
21
+
22
+ # VariableMatrix, optionally module-qualified. The lookahead prevents
23
+ # already-widened annotations from matching again.
24
+ #
25
+ # x: VariableMatrix | None
26
+ # -> x: VariableMatrix | VariableBlock | None
27
+ VARIABLE_MATRIX = re.compile(
28
+ rf"""
29
+ (?<![\w.]) # Not part of another name
30
+ ({PREFIX}) # Optional module prefix
31
+ VariableMatrix\b # Type name
32
+ (?!\ \|\ {PREFIX}VariableBlock\b) # Not already widened
33
+ """,
34
+ re.VERBOSE,
35
+ )
36
+
37
+
38
+ def is_callable(annotation: ast.expr) -> bool:
39
+ """Returns True if an annotation contains a Callable type."""
40
+ return any(
41
+ (isinstance(node, ast.Name) and node.id == "Callable")
42
+ or (isinstance(node, ast.Attribute) and node.attr == "Callable")
43
+ for node in ast.walk(annotation)
44
+ )
45
+
46
+
47
+ def parameters(node: ast.FunctionDef) -> list[ast.arg | None]:
48
+ """Returns a function's parameters in signature order."""
49
+ args = node.args
50
+ return [
51
+ *args.posonlyargs,
52
+ None, # Separates positional-only parameters from the rest
53
+ *args.args,
54
+ args.vararg,
55
+ *args.kwonlyargs,
56
+ args.kwarg,
57
+ ]
58
+
59
+
60
+ def signature(node: ast.FunctionDef) -> list[str | None]:
61
+ """Returns a function's parameter annotations as source strings."""
62
+ return [
63
+ None if arg is None or arg.annotation is None else ast.unparse(arg.annotation)
64
+ for arg in parameters(node)
65
+ ]
66
+
67
+
68
+ def widen_signatures(stub: str) -> str:
69
+ """
70
+ Widens every VariableMatrix in function parameter annotations to
71
+ VariableMatrix | VariableBlock.
72
+
73
+ A parameter isn't widened if a sibling overload is identical except it
74
+ takes VariableBlock there; widening it would make that overload
75
+ unreachable.
76
+ """
77
+ # Group overloads by name within each module or class body
78
+ overload_sets: dict[tuple[int, str], list[ast.FunctionDef]] = {}
79
+ for node in ast.walk(ast.parse(stub)):
80
+ for child in getattr(node, "body", []):
81
+ if isinstance(child, ast.FunctionDef):
82
+ overload_sets.setdefault((id(node), child.name), []).append(child)
83
+
84
+ annotations = []
85
+ for overloads in overload_sets.values():
86
+ signatures = [signature(overload) for overload in overloads]
87
+ for overload, sig in zip(overloads, signatures):
88
+ for i, arg in enumerate(parameters(overload)):
89
+ # Callbacks receive VariableMatrix, so their annotations are
90
+ # left alone
91
+ if arg is None or arg.annotation is None or is_callable(arg.annotation):
92
+ continue
93
+
94
+ # Skip if a sibling overload already takes VariableBlock here
95
+ block_sig = sig.copy()
96
+ block_sig[i] = VARIABLE_MATRIX.sub(
97
+ r"\1VariableBlock", ast.unparse(arg.annotation)
98
+ )
99
+ if block_sig != sig and block_sig in signatures:
100
+ continue
101
+
102
+ annotations.append(arg.annotation)
103
+
104
+ # Encode stub string into UTF-8 bytes
105
+ source_bytes = stub.encode()
106
+
107
+ # Compute byte offsets of each source line
108
+ line_byte_offsets = [0]
109
+ for line in source_bytes.splitlines(keepends=True):
110
+ line_byte_offsets.append(line_byte_offsets[-1] + len(line))
111
+
112
+ # Edit back to front so earlier offsets stay valid
113
+ for annotation in sorted(
114
+ annotations, key=lambda node: (node.lineno, node.col_offset), reverse=True
115
+ ):
116
+ # Assertions to satisfy mypy. They can't fail in practice because
117
+ # ast.parse() always sets those fields.
118
+ assert annotation.end_lineno is not None
119
+ assert annotation.end_col_offset is not None
120
+
121
+ # Compute annotation's byte offsets relative to start of file
122
+ start = line_byte_offsets[annotation.lineno - 1] + annotation.col_offset
123
+ end = line_byte_offsets[annotation.end_lineno - 1] + annotation.end_col_offset
124
+
125
+ # Splice widened type annotations into source_bytes
126
+ #
127
+ # \1 is "sleipnir._sleipnir.autodiff." prefix or empty
128
+ widened = VARIABLE_MATRIX.sub(
129
+ r"\1VariableMatrix | \1VariableBlock", source_bytes[start:end].decode()
130
+ )
131
+ source_bytes = source_bytes[:start] + widened.encode() + source_bytes[end:]
132
+ return source_bytes.decode()
133
+
134
+
135
+ def fix_sparse_matrices(stub: str) -> str:
136
+ """
137
+ Replaces scipy.sparse.csc_matrix[float] with
138
+ scipy.sparse.csc_matrix[numpy.float64], since scipy-stubs requires a NumPy
139
+ numeric scalar type argument.
140
+ """
141
+ return stub.replace(
142
+ "scipy.sparse.csc_matrix[float]", "scipy.sparse.csc_matrix[numpy.float64]"
143
+ )
144
+
145
+
146
+ def main() -> None:
147
+ parser = argparse.ArgumentParser(
148
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
149
+ )
150
+ parser.add_argument(
151
+ "stubs",
152
+ nargs="+",
153
+ type=Path,
154
+ help="stub files (.pyi) to fix up in place",
155
+ )
156
+ args = parser.parse_args()
157
+
158
+ for path in args.stubs:
159
+ stub = path.read_text(encoding="utf-8")
160
+ stub = widen_signatures(stub)
161
+ stub = fix_sparse_matrices(stub)
162
+ path.write_text(stub, encoding="utf-8")
163
+
164
+
165
+ if __name__ == "__main__":
166
+ main()
@@ -2,4 +2,4 @@
2
2
 
3
3
  from ._sleipnir import *
4
4
 
5
- __version__ = "0.6.7.dev54"
5
+ __version__ = "0.6.7.dev58"