Skip to content

Use discrete dynamics Jacobians in solvers#179

Merged
astomodynamics merged 1 commit into
masterfrom
fix/discrete-dynamics-jacobians
Jul 8, 2026
Merged

Use discrete dynamics Jacobians in solvers#179
astomodynamics merged 1 commit into
masterfrom
fix/discrete-dynamics-jacobians

Conversation

@astomodynamics

@astomodynamics astomodynamics commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary

This PR makes the first-order dynamics Jacobian contract discrete: getJacobians() now returns Jacobians of the integrated map x_{k+1} = F(x_k, u_k), and solver backward passes consume those matrices directly as the state-transition linearization.

Changes

  • Added a discrete-dynamics autodiff path for Euler, Heun, RK3, and RK4, with a consistency probe against getDiscreteDynamics() and a finite-difference fallback when the autodiff discrete map is not usable.
  • Updated CDDPSolverBase precomputation plus CLDDP, LogDDP, and MSIPDDP direct Jacobian consumers to use the returned discrete STM directly instead of applying I + dt * Fx and dt * Fu.
  • Fixed the pendulum autodiff dynamics sign so it matches the double-valued dynamics.
  • Added regression tests that compare RK4 discrete Jacobians against finite differences of getDiscreteDynamics() and guard against Euler-style rescaling.

Test Plan

  • cmake --build build2 --parallel 4
  • make test from /home/astomodynamics/naiten_ws/cddp-cpp/build2 (153/153 passed)

Related Issues

None.

How to Test

Run the CMake build, then run make test from the generated build directory.

Screenshots/Videos

Not applicable.

Additional Notes

This PR fixes the first-order STM contract. The existing second-order dynamics Hessian path still follows the prior continuous-dynamics Hessian approximation, so a future change should make full DDP second-order terms discrete as well.

Differentiate the integrated discrete dynamics map for first-order Jacobians and pass the resulting STM directly through the DDP solvers.

Add RK4 regressions that compare getJacobians against finite differences of getDiscreteDynamics and guard against Euler-style rescaling.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3020bbb607

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

consistent = (A_ad - A_fd).cwiseAbs().maxCoeff() <= 1e-5 * jac_scale &&
(B_ad - B_fd).cwiseAbs().maxCoeff() <= 1e-5 * jac_scale;
}
discrete_autodiff_status_ = consistent ? 1 : -1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Synchronize the autodiff status cache

When enable_parallel is set, the solver precompute paths call getJacobians() from multiple std::async workers on the same DynamicalSystem instance. This new mutable cache is read and then written during first-use initialization without an atomic, mutex, or std::once_flag, so the first parallel backward pass can data-race while deciding between autodiff and finite-difference Jacobians; that is undefined behavior and can produce inconsistent derivative paths or be flagged by thread sanitizers.

Useful? React with 👍 / 👎.

@astomodynamics
astomodynamics merged commit 59cdcb3 into master Jul 8, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant