sleipnirgroup-jormungandr 0.6.7.dev56__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.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/CMakeLists.txt +34 -11
  2. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/PKG-INFO +1 -1
  3. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/pyproject.toml +22 -0
  4. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_variable_block.cpp +51 -23
  5. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_variable_matrix.cpp +59 -52
  6. {sleipnirgroup_jormungandr-0.6.7.dev56 → 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.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/src/sleipnir/__init__.py +1 -1
  10. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/src/sleipnir/autodiff/__init__.py +1 -3
  11. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/src/sleipnir/optimization/__init__.py +5 -1
  12. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/gradient_test.py +20 -20
  13. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/hessian_test.py +31 -30
  14. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/jacobian_test.py +11 -11
  15. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/variable_matrix_test.py +21 -21
  16. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/autodiff/variable_test.py +3 -3
  17. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/arm_on_elevator_problem_test.py +1 -1
  18. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/cart_pole_ocp_test.py +3 -3
  19. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/cart_pole_problem_test.py +2 -2
  20. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/cart_pole_util.py +8 -3
  21. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/constraints_test.py +4 -4
  22. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/decision_variable_test.py +4 -4
  23. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/differential_drive_ocp_test.py +1 -1
  24. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/differential_drive_problem_test.py +2 -2
  25. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/differential_drive_util.py +8 -3
  26. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/double_integrator_problem_test.py +1 -1
  27. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/flywheel_ocp_test.py +5 -5
  28. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/flywheel_problem_test.py +1 -1
  29. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/linear_problem_test.py +2 -2
  30. {sleipnirgroup_jormungandr-0.6.7.dev56 → 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.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/nonlinear_problem_test.py +7 -7
  33. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/problem_spy_test.py +7 -5
  34. {sleipnirgroup_jormungandr-0.6.7.dev56 → 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.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/solver/exit_status_test.py +15 -15
  37. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/test/optimization/trivial_problem_test.py +3 -3
  38. sleipnirgroup_jormungandr-0.6.7.dev56/python/test/optimization/near.py +0 -2
  39. sleipnirgroup_jormungandr-0.6.7.dev56/python/test/optimization/rk4.py +0 -18
  40. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/LICENSE.txt +0 -0
  41. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/README.md +0 -0
  42. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/SleipnirConfig.cmake.in +0 -0
  43. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/BuildTypes.cmake +0 -0
  44. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/CompilerFlags.cmake +0 -0
  45. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/Pybind11Mkdoc.cmake +0 -0
  46. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/SubdirList.cmake +0 -0
  47. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/cmake/arm-none-eabi.cmake +0 -0
  48. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/expression.hpp +0 -0
  49. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/expression_graph.hpp +0 -0
  50. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/expression_type.hpp +0 -0
  51. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/gradient.hpp +0 -0
  52. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/hessian.hpp +0 -0
  53. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/jacobian.hpp +0 -0
  54. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/sleipnir_base.hpp +0 -0
  55. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/slice.hpp +0 -0
  56. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/variable.hpp +0 -0
  57. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/variable_block.hpp +0 -0
  58. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/autodiff/variable_matrix.hpp +0 -0
  59. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/multistart.hpp +0 -0
  60. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/ocp/dynamics_type.hpp +0 -0
  61. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/ocp/timestep_method.hpp +0 -0
  62. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/ocp/transcription_method.hpp +0 -0
  63. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/ocp.hpp +0 -0
  64. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/problem.hpp +0 -0
  65. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/exit_status.hpp +0 -0
  66. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/ipm.hpp +0 -0
  67. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/ipm_matrix_callbacks.hpp +0 -0
  68. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/iteration_info.hpp +0 -0
  69. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/newton.hpp +0 -0
  70. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/newton_matrix_callbacks.hpp +0 -0
  71. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/options.hpp +0 -0
  72. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/sqp.hpp +0 -0
  73. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/sqp_matrix_callbacks.hpp +0 -0
  74. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/all_finite.hpp +0 -0
  75. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/append_as_triplets.hpp +0 -0
  76. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/bounds.hpp +0 -0
  77. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/dense_kkt_solver.hpp +0 -0
  78. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/feasibility_restoration.hpp +0 -0
  79. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/filter.hpp +0 -0
  80. {sleipnirgroup_jormungandr-0.6.7.dev56 → 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.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/inertia.hpp +0 -0
  82. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/kkt_error.hpp +0 -0
  83. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/kkt_solver.hpp +0 -0
  84. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/problem_scaling.hpp +0 -0
  85. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/sparse_inf_norms.hpp +0 -0
  86. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/optimization/solver/util/sparse_kkt_solver.hpp +0 -0
  87. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/assert.hpp +0 -0
  88. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/concepts.hpp +0 -0
  89. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/empty.hpp +0 -0
  90. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/function_ref.hpp +0 -0
  91. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/intrusive_shared_ptr.hpp +0 -0
  92. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/pool.hpp +0 -0
  93. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/print.hpp +0 -0
  94. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/print_diagnostics.hpp +0 -0
  95. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/profiler.hpp +0 -0
  96. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/scope_exit.hpp +0 -0
  97. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/spy.hpp +0 -0
  98. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/include/sleipnir/util/symbol_exports.hpp +0 -0
  99. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_expression_type.cpp +0 -0
  100. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_gradient.cpp +0 -0
  101. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_hessian.cpp +0 -0
  102. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_jacobian.cpp +0 -0
  103. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/autodiff/bind_variable.cpp +0 -0
  104. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/binders.hpp +0 -0
  105. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/docstrings.hpp +0 -0
  106. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/for_each_type.hpp +0 -0
  107. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/main.cpp +0 -0
  108. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/bind_equality_constraints.cpp +0 -0
  109. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/bind_inequality_constraints.cpp +0 -0
  110. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/bind_problem.cpp +0 -0
  111. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/ocp/bind_dynamics_type.cpp +0 -0
  112. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/ocp/bind_timestep_method.cpp +0 -0
  113. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/ocp/bind_transcription_method.cpp +0 -0
  114. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/solver/bind_exit_status.cpp +0 -0
  115. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/optimization/solver/bind_iteration_info.cpp +0 -0
  116. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/python/cpp/try_cast.hpp +0 -0
  117. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/autodiff/gradient.cpp +0 -0
  118. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/autodiff/hessian.cpp +0 -0
  119. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/autodiff/jacobian.cpp +0 -0
  120. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/autodiff/variable_matrix.cpp +0 -0
  121. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/ocp.cpp +0 -0
  122. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/problem.cpp +0 -0
  123. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/solver/ipm.cpp +0 -0
  124. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/solver/newton.cpp +0 -0
  125. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/optimization/solver/sqp.cpp +0 -0
  126. {sleipnirgroup_jormungandr-0.6.7.dev56 → sleipnirgroup_jormungandr-0.6.7.dev58}/src/util/pool.cpp +0 -0
@@ -301,7 +301,9 @@ if(SLEIPNIR_BUILD_PYTHON)
301
301
  if(DEFINED PY_BUILD_CMAKE_MODULE_NAME)
302
302
  set(PY_DEST ${PY_BUILD_CMAKE_MODULE_NAME})
303
303
  else()
304
- 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
+ )
305
307
  endif()
306
308
 
307
309
  # nanobind dependency
@@ -343,34 +345,55 @@ if(SLEIPNIR_BUILD_PYTHON)
343
345
  LIBRARY DESTINATION ${PY_DEST}
344
346
  )
345
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)
346
357
  nanobind_add_stub(
347
358
  _sleipnir_stub
348
359
  INSTALL_TIME
349
- MARKER_FILE sleipnir/py.typed
350
- MODULE _sleipnir
351
- OUTPUT sleipnir/__init__.pyi
352
- 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}
353
364
  DEPENDS _sleipnir
354
365
  COMPONENT python_modules
355
366
  )
356
367
  nanobind_add_stub(
357
368
  _sleipnir_autodiff_stub
358
369
  INSTALL_TIME
359
- MODULE _sleipnir.autodiff
360
- OUTPUT sleipnir/autodiff/__init__.pyi
361
- PYTHON_PATH $<TARGET_FILE_DIR:_sleipnir>
370
+ MODULE sleipnir._sleipnir.autodiff
371
+ OUTPUT ${PY_DEST}/_sleipnir/autodiff.pyi
372
+ PYTHON_PATH ${PY_DEST_PARENT}
362
373
  DEPENDS _sleipnir
363
374
  COMPONENT python_modules
364
375
  )
365
376
  nanobind_add_stub(
366
377
  _sleipnir_optimization_stub
367
378
  INSTALL_TIME
368
- MODULE _sleipnir.optimization
369
- OUTPUT sleipnir/optimization/__init__.pyi
370
- PYTHON_PATH $<TARGET_FILE_DIR:_sleipnir>
379
+ MODULE sleipnir._sleipnir.optimization
380
+ OUTPUT ${PY_DEST}/_sleipnir/optimization.pyi
381
+ PYTHON_PATH ${PY_DEST_PARENT}
371
382
  DEPENDS _sleipnir
372
383
  COMPONENT python_modules
373
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
+ )
374
397
 
375
398
  if(NOT CMAKE_CROSSCOMPILING)
376
399
  # pybind11_mkdoc dependency
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sleipnirgroup-jormungandr
3
- Version: 0.6.7.dev56
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.dev56"
5
+ __version__ = "0.6.7.dev58"
@@ -3,7 +3,7 @@ import scipy.sparse
3
3
  from .._sleipnir.autodiff import *
4
4
 
5
5
 
6
- def spy(mat: scipy.sparse.csc_matrix):
6
+ def spy(mat: scipy.sparse.csc_matrix) -> None:
7
7
  """
8
8
  Plot the sparsity pattern of a sparse matrix.
9
9
 
@@ -14,7 +14,6 @@ def spy(mat: scipy.sparse.csc_matrix):
14
14
  mat: The sparse matrix.
15
15
  """
16
16
  import matplotlib.pyplot as plt
17
- import matplotlib.ticker as mticker
18
17
  from matplotlib.colors import ListedColormap
19
18
 
20
19
  xs = []
@@ -42,7 +41,6 @@ def spy(mat: scipy.sparse.csc_matrix):
42
41
  # Display colorbar
43
42
  colorbar = fig.colorbar(sc)
44
43
  ticklabels = ["Negative", "Positive"]
45
- colorbar.ax.yaxis.set_major_locator(mticker.FixedLocator(ticklabels))
46
44
  colorbar.ax.set_yticks([-1, 1])
47
45
  colorbar.ax.set_yticklabels(ticklabels)
48
46
 
@@ -1,9 +1,13 @@
1
1
  import concurrent.futures
2
+ from collections.abc import Callable, Sequence
2
3
 
3
4
  from .._sleipnir.optimization import *
4
5
 
5
6
 
6
- def multistart(solve, initial_guesses):
7
+ def multistart[T](
8
+ solve: Callable[[T], tuple[ExitStatus, float, T]],
9
+ initial_guesses: Sequence[T],
10
+ ) -> tuple[ExitStatus, float, T]:
7
11
  """
8
12
  Solves an optimization problem from different starting points in parallel,
9
13
  then returns the solution with the lowest cost.