Skip to content

feat(models): mountain_car with a free final time, in the ModelingToolkit modeling (#308) - #452

Draft
ocots wants to merge 2 commits into
feat/mtk-more-problemsfrom
feat/mtk-free-time
Draft

ocots wants to merge 2 commits into
feat/mtk-more-problemsfrom
feat/mtk-free-time

Conversation

@ocots

@ocots ocots commented Nov 11, 2026

Copy link
Copy Markdown
Member

Refs #308. Stacked on #451: its base is feat/mtk-more-problems; retarget it to main once #451 is merged. Applied conventions: ct-conventions v0.12.0, ct-agents v0.4.0.

An experiment, to decide whether the problems with a free final time can be written in the ModelingToolkit modeling: mountain_car first. Eleven of the thirteen problems that #451 leaves have a variable tf.

The idea

Corleone integrates on a fixed horizon, so tf cannot be an unknown of the System. The ModelingToolkit model of such a problem is written in the reduced time s = (t - t0) / (tf - t0) on [0, 1], with the final time tf as a parameter to optimize (tunable, with its bounds), the derivatives and the integrands multiplied by the duration tf - t0. It is an exact rewriting, and it is what SciML's catalogue does with a parameter T (hang_glider, goddarts_rocket).

The OptimalControl and JuMP models are not touched. Only the ModelingToolkit files and the test helpers change. A page of the documentation will explain the reduced time, as the JuMP schemes have theirs (a separate pull request).

ModelingToolkit.@parameters tf [tunable = true, bounds = (tf_min, Inf)]
eqs = [D(pos(t)) ~ (tf - t0) * vel(t), D(vel(t)) ~ (tf - t0) * (a * u(t) - b * cos(c * pos(t)))]
costs = [tf]                                   # a Mayer cost: the final time
constraints = [pos(1.0) ~ pos_tf, vel(1.0) ≳ vel_tf_min, ...]   # the final conditions are at s = 1

The parameter is named tf, as the variable of the OptimalControl and JuMP models, so that variable_components and the start of the solve (init.variable) need no mapping.

What changes in the tests

  • SolveModelingToolkit.free_final_time, times (physical (t0, tf), tf is nothing when free) and tspan ((0, 1) when free).
  • prepare starts the variables from the guess of the problem (variable_start), as the controls and the free initial states.
  • The trajectory goes back to the physical time t0 + (tf - t0) s, and records tf as the variable.
  • The continuous comparison (ContinuousModelingToolkit.problem_data) reads tf as the variable, with times = v -> (t0, v[1]), and divides the dynamics and the integrand by the duration, evaluated at the reduced time of the point.

Convention for the models written this way

The parameter is tf; the horizon of the System is [0, 1]; the dynamics and the integrands carry the factor tf - t0; a time that appears explicitly is t0 + (tf - t0) * s; the final conditions are written at s = 1.

Test plan

  • Locally (Julia 1.12): Pkg.test(; test_args=["ModelingToolkit", "problem=mountain_car,bryson_denham,bioreactor"]): 170 passed (test_methods 51, test_modelings 50, test_problem 66, test_smoke 3), and core passes.
  • Sensitivity of the comparison on mountain_car: a changed fails dynamics, vel_tf_min fails boundary, tf_min fails variable_bounds.
  • Corleone accepts tf as a parameter to optimize: its bound and the bounds of the control are in the problem (lb, ub), and the objective moves in a solve of a few iterations.
  • CI with the label test ModelingToolkit backend.
  • To check on the first cron: the convergence of mountain_car (a solve on the default grid).

Open

  • A problem whose time appears explicitly (bioreactor, double_oscillator and others are fixed-time here; among the free-time ones, which have an explicit time?).
  • A free initial time: none of ours.
  • The documentation: the page of the reduced time (separate pull request).

🤖 Generated with Claude Code

ocots and others added 2 commits November 11, 2026 19:04
…in the physical time (#308)

Corleone integrates on a fixed horizon. A problem with a variable tf is written on [0, 1] in
s = (t - t0) / (tf - t0), with tf a parameter to optimize and the derivatives and integrands
multiplied by the duration tf - t0. The solve helper starts tf from the guess of the problem and
puts the trajectory back on the physical time; the continuous comparison reads tf as the
variable of the problem and divides the functions by the duration.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ee final time (#308)

A Mayer cost on the final time, written in the reduced time. Passes the continuous comparison
with OptimalControl, which fails when the dynamics, the final condition or the bound of tf is
changed, and the smoke solve.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@ocots ocots added test ModelingToolkit backend Package-specific scope or cron label run ci github-runner Trigger the CI workflow on GitHub-hosted runners labels Nov 11, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run ci github-runner Trigger the CI workflow on GitHub-hosted runners test ModelingToolkit backend Package-specific scope or cron label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant