[Tests] Corleone solve helper and ModelingToolkit test suite (smoke, equivalence, reference solution) (#308)

未关闭
#438 0 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

维护者通常 1 天内回复

@ocots 已经在做这个了。

开始于 2026年11月8日。

评估

这个 Issue 还没有评估数据。

描述

feature

Agents: read the guide for agents in #310 first (order, difficulty, how to claim an issue, working method). Read AGENTS.md and clone .ct-conventions (v0.11.0) and .ct-agents (v0.4.0). Specification: this description and #308.

Sub-issue of #308, part 2 of 3. Depends on #437 (ModelingToolkitBackend and continuous bryson_denham model).


Goal

Implement the Corleone-based solve helper in test/common/solve_modelingtoolkit.jl (keeping Corleone strictly in the test environment, outside the package extension), complete the test/suite/ModelingToolkit/ test suite (test_methods.jl, test_smoke.jl, test_equivalence.jl, test_reference_solution.jl), and promote bryson_denham from :draft to :valid for ModelingToolkit.

At the end of this issue, bryson_denham(ModelingToolkitBackend()) is solved with Corleone + Ipopt using the shared :init from metadata, cross-evaluated against OptimalControl, verified against the analytic reference solution (:reference_solution), marked :valid, and all suite/ModelingToolkit and suite/core tests pass.


Technical Findings (from #308 preliminary checks)

  1. Discretizing continuous path constraints (b) for Corleone:
    Corleone.CorleoneDynamicOptProblem(sys, ...) (Corleone/ext/MTKExtension/utils.jl:75) requires every signal call x(τ) in ModelingToolkit.get_constraints(sys) to have a concrete finite real number τ (time isa Real && isfinite(time)), throwing ArgumentError("Signal times must be finite real numbers: x₁(t).") on symbolic t = ModelingToolkit.get_iv(sys).
    In test/common/solve_modelingtoolkit.jl, before calling CorleoneDynamicOptProblem, inspect ModelingToolkit.get_constraints(sys):
    • Keep point constraints (neither lhs nor rhs contains iv = ModelingToolkit.get_iv(sys) as a free variable, e.g. x₁(tf) ~ x1_tf) as-is.
    • Expand any path constraint (whose lhs or rhs contains iv, e.g. x₁(t) ≲ x1_max) by Symbolics.substitute(c, Dict(iv => τ)) for each τ in constraint_grid (filtering out t0 if initial state is fixed), and rebuild a ModelingToolkit.System with the discretized constraints.
  2. Extracting tspan = (t0, tf) from sys:
    Extract (t0, tf) from the Symbolics.Integral domain in ModelingToolkit.get_costs(sys) (DomainSets.infimum / supremum or interval.left / interval.right).
  3. Injecting shared :init (#306) into model.ps:
    In model = Corleone.CorleoneDynamicOptProblem(sys_disc, tspan, ...; switches=control_grid[2:(end - 1)], shooting=shooting_grid, alg=OrdinaryDiffEqTsit5.Tsit5()):
    • model.ps has fields (initialcondition = ..., parameter = ...).
    • Controls (model.ps.parameter.parameters): for N = length(control_grid) - 1 piecewise-constant intervals [t_k, t_{k+1}], evaluate init.control at the interval midpoints (or left endpoints) and write into each control's .ps vector of length N.
    • States (model.ps.initialcondition): in single shooting with fixed initial state (tunable = false), model.ps.initialcondition is empty; in multiple shooting, evaluate init.state at the shooting nodes and write into model.ps.initialcondition (or via Corleone.custom_initialization).
    • Pass the updated ps to Optimization.OptimizationProblem(model, Optimization.AutoForwardDiff(); ps=ps) so opt.u0 uses our shared initial guess.
  4. Cross-evaluation (test_equivalence.jl) with imposed parameters:
    Corleone exposes:
    • Corleone.optimization_objective(model, ps, model.st) -> scalar objective $J$.
    • Corleone.optimization_constraints(model, ps, model.st) -> constraint vector $c$, bounded by model.lcons and model.ucons. Constraint violation is maximum(max.(model.lcons .- c, c .- model.ucons, 0.0); init=0.0).
      Project the optimal control of OptimalControl (or :reference_solution) onto Corleone's piecewise-constant control_grid (using interval midpoints (t_k + t_{k+1}) / 2), evaluate optimization_objective and optimization_constraints, and verify that objective and constraint violation agree within a grid-step tolerance $O(h)$ / $O(h^2)$ where $h = (t_f - t_0)/N$.

Mandatory Rules & Conventions (ct-conventions v0.11.0, #310)

  • Worktree per issue (#310 rule 3a): Never edit or switch branches in the main checkout. Use a dedicated worktree (git worktree add ../OptimalControlProblems-<issue> -b feat/<issue>-mtk-corleone-tests main).
  • Separation of modeling and solving: Corleone must not be added to the root Project.toml or ext/ModelingToolkitModels.jl; it belongs only in test/suite/ModelingToolkit/Project.toml and test/common/solve_modelingtoolkit.jl.
  • Qualified imports only (MOD3, MOD4): Always use using Pkg: Pkg, sym. Never use bare using Pkg or import.
  • Fake types at module top-level (TST3): Any helper structs in tests must be defined at module top-level, never inside @testset or functions.
  • No hand-written problem lists (test_no_handwritten_list in test/suite/core/test_consistency.jl): Never write :bryson_denham or "bryson_denham" literals in test/! Always loop with Main.TestStatus.for_each_problem(..., OptimalControlProblems.ModelingToolkitBackend()).

Detailed Implementation Plan

Files to Create or Modify
File Action Details
test/suite/ModelingToolkit/Project.toml Modify Add Corleone (compat = "2"), OrdinaryDiffEqTsit5 ("2"), Optimization ("5"), OptimizationMOI ("1"), Ipopt ("1"), ComponentArrays ("0.15"), ForwardDiff ("1"), OptimalControl ("2.2"), NLPModelsIpopt ("0.11"), Symbolics, SciMLBase.
test/common/configs.jl Modify Add ModelingToolkit = [(shooting=:single, solver=:ipopt)] to CONFIGS.
test/common/status.jl Modify Add "ModelingToolkit", "Corleone", "OrdinaryDiffEqTsit5" to KEY_DEPENDENCIES.
test/common/solve_modelingtoolkit.jl Create Module SolveModelingToolkit:
- _extract_tspan(sys)
- _build_grids(tspan; grid_size, shooting, control_grid, constraint_grid, shooting_grid)
- _discretize_path_constraints(sys, constraint_grid)
- _apply_initial_guess!(model, sys, init, control_grid, shooting_grid)
- prepare(pb; shooting=:single, grid_size=nothing, ...)
- solve(pb; shooting=:single, solver=:ipopt, grid_size=nothing, max_iter=1000, tol=1e-6, print_level=0, ...)
- evaluate(pb, control_fn; state_fn=nothing, grid_size=nothing, shooting=:single) returning (; objective, constraint_violation, grid_step)
- outcome(sol, model, reconstruct), settings(pb, cfg; grid_size=nothing), trajectory(sol, model, reconstruct, sys)
test/suite/ModelingToolkit/test_methods.jl Create Unit-test SolveModelingToolkit: _extract_tspan, _build_grids (single & multiple shooting), _discretize_path_constraints, _apply_initial_guess!, settings, outcome, trajectory.
test/suite/ModelingToolkit/test_smoke.jl Create Smoke test every problem in ModelingToolkitBackend() across CONFIGS.ModelingToolkit with grid_size = TestConfigs.SMALL_GRID (20) and max_iter = TestConfigs.SMOKE_MAX_ITER (10): check no exception and isfinite(objective).
test/suite/ModelingToolkit/test_equivalence.jl Create Cross-evaluation test: for each problem in ModelingToolkitBackend(), project the reference optimal control onto control_grid (midpoint sampling), call SolveModelingToolkit.evaluate, and assert that objective matches the reference objective within $O(h)$ and constraint_violation is within $O(h^2)$.
test/suite/ModelingToolkit/test_reference_solution.jl Create For each problem in ModelingToolkitBackend() with has_reference_solution(problem), solve with Corleone + Ipopt (grid_size=50, tol=1e-6) and check objective and state/control $L^\infty$ errors against reference_solution_data(problem).
ext/MetaData/bryson_denham.jl Modify Switch ModelingToolkit status from :draft to :valid.

Verification Commands

julia --project=. -e 'using Pkg; Pkg.test(test_args=["suite/ModelingToolkit"])'
julia --project=. -e 'using Pkg; Pkg.test(test_args=["suite/core"])'

Done when

  • test/common/solve_modelingtoolkit.jl discretizes continuous path constraints onto constraint_grid, injects shared :init into model.ps, and solves with Corleone + Ipopt.
  • test_methods.jl, test_problem.jl, test_smoke.jl, test_equivalence.jl (cross-evaluation), and test_reference_solution.jl all pass in suite/ModelingToolkit.
  • bryson_denham status for ModelingToolkit is :valid in ext/MetaData/bryson_denham.jl.
  • suite/core passes (including test_consistency.jl and test_no_handwritten_list).

Refs #308, #310

主要语言
Julia
星标
13
派生
1
平均合并
3 小时 33 分钟
30 天内合并 PR
55

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

control-toolbox/OptimalControlProblems.jl 的其他 Issue

查看 control-toolbox/OptimalControlProblems.jl 的全部 Issue

相似的 Issue

更多 Julia Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。