From 5a96685c86a9786470d4ab288656653349ab9aca Mon Sep 17 00:00:00 2001 From: Tyler Veness Date: Thu, 9 Apr 2026 17:03:57 -0700 Subject: [PATCH] [upstream_utils] Upgrade to Sleipnir 0.5.2 This fixes a bug in Sleipnir's Newton solver (the exit status was inaccurate because unconstrained optimization problems can't be infeasible). --- upstream_utils/sleipnir.py | 2 +- .../sleipnir_patches/0001-Use-fmtlib.patch | 8 +++---- .../0006-Replace-std-unreachable.patch | 4 ++-- .../math/optimization/solver/ExitStatus.java | 23 +++++++++++-------- .../optimization/solver/exit_status.hpp | 18 +++++++++------ .../sleipnir/optimization/solver/newton.hpp | 4 ++-- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/upstream_utils/sleipnir.py b/upstream_utils/sleipnir.py index 4b7287cb92..49d27d17cd 100755 --- a/upstream_utils/sleipnir.py +++ b/upstream_utils/sleipnir.py @@ -46,7 +46,7 @@ using small_vector = wpi::util::SmallVector; def main(): name = "sleipnir" url = "https://github.com/SleipnirGroup/Sleipnir" - tag = "v0.5.1" + tag = "v0.5.2" sleipnir = Lib(name, url, tag, copy_upstream_src) sleipnir.main() diff --git a/upstream_utils/sleipnir_patches/0001-Use-fmtlib.patch b/upstream_utils/sleipnir_patches/0001-Use-fmtlib.patch index 226de84d9e..d61a30a529 100644 --- a/upstream_utils/sleipnir_patches/0001-Use-fmtlib.patch +++ b/upstream_utils/sleipnir_patches/0001-Use-fmtlib.patch @@ -78,7 +78,7 @@ index a9553ffbcfed568c48f7d789d8b127790dfddb91..3de6d5bf89a65fe07784350c3b1a4691 #include "sleipnir/autodiff/expression_type.hpp" diff --git a/include/sleipnir/optimization/solver/exit_status.hpp b/include/sleipnir/optimization/solver/exit_status.hpp -index 01dddd0d4967268cbbb2c808f7973fafc6be081d..8a7904dd5664dcd66d1332f38902df2252387a58 100644 +index 0a48df7423b5a3dccd8e611e91befd32487fafdc..8786d6d64ac44ac88133df65a79636ec133b1b64 100644 --- a/include/sleipnir/optimization/solver/exit_status.hpp +++ b/include/sleipnir/optimization/solver/exit_status.hpp @@ -4,9 +4,10 @@ @@ -93,7 +93,7 @@ index 01dddd0d4967268cbbb2c808f7973fafc6be081d..8a7904dd5664dcd66d1332f38902df22 namespace slp { /// Solver exit status. Negative values indicate failure. -@@ -42,14 +43,16 @@ enum class ExitStatus : int8_t { +@@ -45,14 +46,16 @@ enum class ExitStatus : int8_t { } // namespace slp @@ -112,7 +112,7 @@ index 01dddd0d4967268cbbb2c808f7973fafc6be081d..8a7904dd5664dcd66d1332f38902df22 return m_underlying.parse(ctx); } -@@ -60,7 +63,8 @@ struct std::formatter { +@@ -63,7 +66,8 @@ struct std::formatter { /// @param ctx Format context. /// @return Format context iterator. template @@ -122,7 +122,7 @@ index 01dddd0d4967268cbbb2c808f7973fafc6be081d..8a7904dd5664dcd66d1332f38902df22 using enum slp::ExitStatus; switch (exit_status) { -@@ -93,5 +97,7 @@ struct std::formatter { +@@ -97,5 +101,7 @@ struct std::formatter { } private: diff --git a/upstream_utils/sleipnir_patches/0006-Replace-std-unreachable.patch b/upstream_utils/sleipnir_patches/0006-Replace-std-unreachable.patch index 91f23f31fe..6172fb5d6a 100644 --- a/upstream_utils/sleipnir_patches/0006-Replace-std-unreachable.patch +++ b/upstream_utils/sleipnir_patches/0006-Replace-std-unreachable.patch @@ -37,7 +37,7 @@ index d06d32dac6c7b6faeedefeaa107cedac8446a3ab..1957fc0339b5538fbdc56a8ea2d8503c } diff --git a/include/sleipnir/optimization/solver/exit_status.hpp b/include/sleipnir/optimization/solver/exit_status.hpp -index 8a7904dd5664dcd66d1332f38902df2252387a58..ee9bfbc7630513a33297dbeaf39fd695509b5a97 100644 +index 8786d6d64ac44ac88133df65a79636ec133b1b64..d1bf7e62aa844003cfcd7f3df6fd9c9a65563586 100644 --- a/include/sleipnir/optimization/solver/exit_status.hpp +++ b/include/sleipnir/optimization/solver/exit_status.hpp @@ -4,10 +4,10 @@ @@ -53,7 +53,7 @@ index 8a7904dd5664dcd66d1332f38902df2252387a58..ee9bfbc7630513a33297dbeaf39fd695 namespace slp { /// Solver exit status. Negative values indicate failure. -@@ -92,7 +92,7 @@ struct fmt::formatter { +@@ -96,7 +96,7 @@ struct fmt::formatter { case TIMEOUT: return m_underlying.format("timeout", ctx); default: diff --git a/wpimath/src/main/java/org/wpilib/math/optimization/solver/ExitStatus.java b/wpimath/src/main/java/org/wpilib/math/optimization/solver/ExitStatus.java index 705bcfd319..7d6bdb1351 100644 --- a/wpimath/src/main/java/org/wpilib/math/optimization/solver/ExitStatus.java +++ b/wpimath/src/main/java/org/wpilib/math/optimization/solver/ExitStatus.java @@ -18,21 +18,23 @@ public enum ExitStatus { GLOBALLY_INFEASIBLE(-3), /** The linear system factorization failed. */ FACTORIZATION_FAILED(-4), + /** The backtracking line search failed, and the problem isn't locally infeasible. */ + LINE_SEARCH_FAILED(-5), /** * The solver failed to reach the desired tolerance, and feasibility restoration failed to * converge. */ - FEASIBILITY_RESTORATION_FAILED(-5), + FEASIBILITY_RESTORATION_FAILED(-6), /** The solver encountered nonfinite initial cost, constraints, or derivatives and gave up. */ - NONFINITE_INITIAL_GUESS(-6), + NONFINITE_INITIAL_GUESS(-7), /** The solver encountered diverging primal iterates xₖ and/or sₖ and gave up. */ - DIVERGING_ITERATES(-7), + DIVERGING_ITERATES(-8), /** The solver returned its solution so far after exceeding the maximum number of iterations. */ - MAX_ITERATIONS_EXCEEDED(-8), + MAX_ITERATIONS_EXCEEDED(-9), /** * The solver returned its solution so far after exceeding the maximum elapsed wall clock time. */ - TIMEOUT(-9); + TIMEOUT(-10); /** ExitStatus value. */ public final int value; @@ -55,11 +57,12 @@ public enum ExitStatus { case -2 -> ExitStatus.LOCALLY_INFEASIBLE; case -3 -> ExitStatus.GLOBALLY_INFEASIBLE; case -4 -> ExitStatus.FACTORIZATION_FAILED; - case -5 -> ExitStatus.FEASIBILITY_RESTORATION_FAILED; - case -6 -> ExitStatus.NONFINITE_INITIAL_GUESS; - case -7 -> ExitStatus.DIVERGING_ITERATES; - case -8 -> ExitStatus.MAX_ITERATIONS_EXCEEDED; - case -9 -> ExitStatus.TIMEOUT; + case -5 -> ExitStatus.LINE_SEARCH_FAILED; + case -6 -> ExitStatus.FEASIBILITY_RESTORATION_FAILED; + case -7 -> ExitStatus.NONFINITE_INITIAL_GUESS; + case -8 -> ExitStatus.DIVERGING_ITERATES; + case -9 -> ExitStatus.MAX_ITERATIONS_EXCEEDED; + case -10 -> ExitStatus.TIMEOUT; default -> null; }; } diff --git a/wpimath/src/main/native/thirdparty/sleipnir/include/sleipnir/optimization/solver/exit_status.hpp b/wpimath/src/main/native/thirdparty/sleipnir/include/sleipnir/optimization/solver/exit_status.hpp index ee9bfbc763..d1bf7e62aa 100644 --- a/wpimath/src/main/native/thirdparty/sleipnir/include/sleipnir/optimization/solver/exit_status.hpp +++ b/wpimath/src/main/native/thirdparty/sleipnir/include/sleipnir/optimization/solver/exit_status.hpp @@ -25,20 +25,23 @@ enum class ExitStatus : int8_t { GLOBALLY_INFEASIBLE = -3, /// The linear system factorization failed. FACTORIZATION_FAILED = -4, + /// The backtracking line search failed, and the problem isn't locally + /// infeasible. + LINE_SEARCH_FAILED = -5, /// The solver failed to reach the desired tolerance, and feasibility /// restoration failed to converge. - FEASIBILITY_RESTORATION_FAILED = -5, + FEASIBILITY_RESTORATION_FAILED = -6, /// The solver encountered nonfinite initial cost, constraints, or derivatives /// and gave up. - NONFINITE_INITIAL_GUESS = -6, + NONFINITE_INITIAL_GUESS = -7, /// The solver encountered diverging primal iterates xₖ and/or sₖ and gave up. - DIVERGING_ITERATES = -7, + DIVERGING_ITERATES = -8, /// The solver returned its solution so far after exceeding the maximum number /// of iterations. - MAX_ITERATIONS_EXCEEDED = -8, + MAX_ITERATIONS_EXCEEDED = -9, /// The solver returned its solution so far after exceeding the maximum /// elapsed wall clock time. - TIMEOUT = -9, + TIMEOUT = -10, }; } // namespace slp @@ -80,11 +83,12 @@ struct fmt::formatter { return m_underlying.format("globally infeasible", ctx); case FACTORIZATION_FAILED: return m_underlying.format("factorization failed", ctx); + case LINE_SEARCH_FAILED: + return m_underlying.format("line search failed", ctx); case FEASIBILITY_RESTORATION_FAILED: return m_underlying.format("feasibility restoration failed", ctx); case NONFINITE_INITIAL_GUESS: - return m_underlying.format( - "nonfinite initial cost, constraints, or derivatives", ctx); + return m_underlying.format("nonfinite initial guess", ctx); case DIVERGING_ITERATES: return m_underlying.format("diverging iterates", ctx); case MAX_ITERATIONS_EXCEEDED: diff --git a/wpimath/src/main/native/thirdparty/sleipnir/include/sleipnir/optimization/solver/newton.hpp b/wpimath/src/main/native/thirdparty/sleipnir/include/sleipnir/optimization/solver/newton.hpp index 31acf4fe33..ebc7d0b62c 100644 --- a/wpimath/src/main/native/thirdparty/sleipnir/include/sleipnir/optimization/solver/newton.hpp +++ b/wpimath/src/main/native/thirdparty/sleipnir/include/sleipnir/optimization/solver/newton.hpp @@ -204,7 +204,7 @@ ExitStatus newton( α *= α_reduction_factor; if (α < α_min) { - return ExitStatus::LOCALLY_INFEASIBLE; + return ExitStatus::LINE_SEARCH_FAILED; } continue; } @@ -236,7 +236,7 @@ ExitStatus newton( break; } - return ExitStatus::LOCALLY_INFEASIBLE; + return ExitStatus::LINE_SEARCH_FAILED; } }