Rationalize JuMP and comparison test suites: separate generic transcription unit tests from per-problem modeling checks
Maintainer antworten meist innerhalb von 1 Tag
@ocots arbeitet bereits daran.
Seit 08.11.2026.
Bewertung
Dieses Issue wurde noch nicht bewertet.
Beschreibung
Context and Motivation
Since #368, #369, and #370, JuMP models in ext/JuMPModels/ are no longer transcribed problem by problem:
- Each problem file (
ext/JuMPModels/<problem>.jl) only declares aProblemDatastruct (components, bounds, times, dynamics, Lagrange/Mayer costs, path and boundary constraints) and delegates model construction tobuild. - The transcription itself (
ext/JuMPModels/schemes/:data.jl,tableaux.jl,build.jl) and the solution accessors / builder (ext/JuMPModels.jl:state,control,costate,variable,time_grid,solution) are completely generic.
In #401, test/suite/comparison/test_equivalence.jl was updated to reflect this separation: it checks :trapeze on every problem (to verify that each problem's modeling matches OptimalControl), and checks all schemes on a small subset of problems selected by their features (every_scheme).
However, the rest of test/suite/JuMP/ and test/suite/comparison/ still loops over the Cartesian product all problems × all schemes (× all orderings), running hundreds of redundant JuMP model builds, CTDirect discretizations, and max_iter=0 Ipopt/MadNLP solves that test the generic transcription code over and over instead of testing problem-specific modeling.
Goals
- Unit-test the generic JuMP transcription once (
test/suite/JuMP/):- Test
ButcherTableautraits, scheme equations, convergence orders, variable/constraint orderings (:interleaved,:blocks), the model dictionary contract (:scheme,:ordering,:time_grid,:constraints, container lengths),_start!initial guess loading,JuMP.Modelargument forwarding, and error paths (IncorrectArgument,NotImplemented) on small toyProblemDatadefinitions.
- Test
- Test each problem's modeling once per problem (
test/suite/JuMP/andtest/suite/comparison/):- Verify that each problem's
ProblemData, metadata parameters, and default initial guess are well-formed and consistent on a single default build (without looping over schemes/orderings or launching a solver just to read the initial time grid). - Keep
test_smoke.jl(SMOKE_GRID,SMOKE_MAX_ITER) on every problem so every problem is shown to build and run a few solver iterations. - Verify NLP equivalence with
OptimalControl(CTDirect) on every problem using a single configuration (scheme=:trapeze,ordering=:interleaved), plus component name agreement (state_components,control_components,variable_components).
- Verify that each problem's
- Test transcription & solution integration with
OptimalControl.jlon a representative subset of problems (test/suite/comparison/):- Use feature-based selection (covering cost types, fixed/free times, scalar vs. vector state/control/variable per
DIM1, box/path/boundary constraints) to test all4 schemes × 2 orderingsagainstCTDirectand to testOCP.solution(model, pb)and its interpolations (:linearvs.:constant).
- Use feature-based selection (covering cost types, fixed/free times, scalar vs. vector state/control/variable per
- Preserve
cron/androbustness/behavior while sharing common test runners (test/common/):- Keep
test/cron/andtest/robustness/solving all problems on their default grids and emitting the exact report tables and.tsv/.tomlartifacts expected bytools/cron/report.py. - Factor out duplicated test code across
OptimalControlandOptimalControlScalarized(test_problem.jl,test_smoke.jl,test_convergence.jl) into shared helpers intest/common/, while preservingtools/ci/scope.pydependency detection (Repository.includers).
- Keep
Current State and Redundancies (for $P = 22$ problems)
1. test/suite/JuMP/
test_contract.jl:- Current: Builds $P \times 4 \text{ schemes} \times 2 \text{ orderings} = 176$ JuMP models, runs Ipopt with
max_iter=0whenevertfis free to evaluatemodel[:time_grid](), and runs $2P = 44$ error checks (scheme=:gauss_legendre_2,ordering=:random). - Redundancy: Keys like
:scheme,:ordering,:constraints, container sizes ($N+1$, $N$, $M$), and unknown scheme/ordering errors are determined solely byschemes/build.jl, not by<problem>.jl.
- Current: Builds $P \times 4 \text{ schemes} \times 2 \text{ orderings} = 176$ JuMP models, runs Ipopt with
test_kwargs.jl:- Current: Builds $3P = 66$ JuMP models to verify that
Ipopt.Optimizerandadd_bridges=trueare forwarded toJuMP.Modeland that passinginit = 2 * guessdoublesJuMP.start_value. - Redundancy: Keyword forwarding and
_start!are generic functions inschemes/build.jl. Only checking that the problem's default metadatainitpopulates all start values is problem-specific.
- Current: Builds $3P = 66$ JuMP models to verify that
test_tableaux.jl,test_build.jl,test_signs.jl,test_draft.jl,test_methods.jl,test_smoke.jl:- Already targeted and non-redundant.
2. test/suite/comparison/
test_equivalence.jl&test/common/equivalence.jl:- Current: Tests
:trapezeon every problem and all 4 schemes onselected = every_scheme(both, features). However,Equivalence.test_jumpstill loops over bothORDERINGS = (:interleaved, :blocks)for every problem, even though:blocksonly permutes variable/constraint creation order inschemes/build.jl.
- Current: Tests
test_start.jl:- Current: Loops over $P \times 4 \text{ schemes} = 88$ pairs, building the JuMP model and calling
OptimalControl.discretize+OptimalControl.nlp_model(..., ADNLP())to compareJuMP.start_valuewithnlp.meta.x0. - Redundancy:
Equivalence.compareandconsistent_nlpsintest_equivalence.jlalready comparemeta.x0(:x0) on every problem for:trapezeand on the representative subset for all 4 schemes.
- Current: Loops over $P \times 4 \text{ schemes} = 88$ pairs, building the JuMP model and calling
test_init.jl(test/common/comparison.jl):- Current: Runs Ipopt/MadNLP with
max_iter=0onSMALL_GRID = 50for every problem acrossJuMP,OptimalControl, andOptimalControlScalarized($3P = 66$ solves) to comparenvar,ncon, component names, time grid, initial trajectories, and initial objective. - Redundancy:
test_equivalence.jlalready checksnvar,ncon, bounds,x0, objective, constraints, gradient, Jacobian, and Hessian atx0to1e-10without invoking any solver. Only the component names (state_components,control_components,variable_components) are specific totest_init.jl, and they can be compared directly on the models without solving.
- Current: Runs Ipopt/MadNLP with
test_solution.jl:- Current: Loops over $P \times 4 \text{ schemes} = 88$ JuMP models on
SMALL_GRID = 50, running Ipopt withmax_iter=0on each to check thatOCP.solution(model, pb)andTestStatus.trajectorymatch the JuMP accessors, plus a full solve onbeamfor the 4 schemes. - Redundancy:
OCP.solution(model, pb)and the JuMP accessors inext/JuMPModels.jlare completely generic. Testing them on a representative subset of problems (covering scalar vs. vector state/control and $0, 1, >1$ variables) across the 4 schemes is sufficient.
- Current: Loops over $P \times 4 \text{ schemes} = 88$ JuMP models on
3. Duplicated files across OptimalControl and OptimalControlScalarized
test/suite/OptimalControl/test_problem.jlandtest/suite/OptimalControlScalarized/test_problem.jlare identical except for the backend constant.test/suite/OptimalControl/test_smoke.jlandtest/suite/OptimalControlScalarized/test_smoke.jlare identical except for the backend constant.test/cron/OptimalControl/test_convergence.jlandtest/cron/OptimalControlScalarized/test_convergence.jlare identical except for the backend constant.
Proposed Solutions
1. Move generic contract and _start! checks to test/suite/JuMP/test_build.jl
- In
test_build.jl:- Check the full model dictionary contract (
:scheme,:ordering,:time_grid,:constraints, component keys and container lengths $N+1$, $M$, $N$, $1$) across all4 schemes × 2 orderingsonFAKE, plus theIncorrectArgumentthrows on unknownschemeandordering. - Unit-test
_start!(constant, vector, time-dependent function, custominitoverride, free final time) andJuMP.Modelargument/keyword forwarding (Ipopt.Optimizer,add_bridges).
- Check the full model dictionary contract (
- Simplify
test_contract.jl(or mergetest_kwargs.jlinto it):- Build one model per problem (
grid_size=2, default:trapezeand:interleaved). - Check problem-specific modeling invariants: non-empty component names,
length(costate_components) == length(state_components), all variables have a finiteJuMP.start_value, and the initial time grid evaluated at the start values is sorted withT[1] < T[end](no Ipoptmax_iter=0call needed).
- Build one model per problem (
2. Streamline test/suite/comparison/
- Shared representative problem selector:
- Extract the feature-based representative problem selection (
every_schemeintest_equivalence.jl, extended if needed to ensure scalar/vector state, control, and variable dimensions0, 1, >1are covered) into a shared helper intest/common/.
- Extract the feature-based representative problem selection (
test_equivalence.jl&test/common/equivalence.jl:- On every problem: test
(scheme=:trapeze, ordering=:interleaved)againstCTDirect(ADNLPandExa), which directly checks problem modeling equivalence (includingmeta.x0). - On the representative subset: test all
4 schemes × 2 orderings.
- On every problem: test
test_features.jl:- In addition to comparing
features(vector) == features(scalarized), check that component names (state_components,control_components,variable_components) and dimensions agree betweenJuMP,OptimalControl, andOptimalControlScalarizedfor every problem (no solver call).
- In addition to comparing
test_start.jlandtest_init.jl:- Remove
test_start.jl(sincemeta.x0equivalence is already checked bytest_equivalence.jlon every problem for:trapezeand on the representative subset for all 4 schemes, and_start!is unit-tested intest_build.jl). - Remove
test_init.jl(since NLP evaluation atx0is checked to1e-10bytest_equivalence.jland component names are checked bytest_features.jl). KeepComparison.within/Comparison.within_L2(used bytest_solution.jl).
- Remove
test_solution.jl:- Run
check_solution(problem, scheme)across the 4 schemes on the representative subset of problems (plusbeamsolved againstOptimalControl), instead of all $P \times 4$ pairs.
- Run
3. Factorize shared OptimalControl / OptimalControlScalarized runners
- Extract shared helpers in
test/common/fortest_problem,test_smoke, andtest_convergencesoOptimalControlandOptimalControlScalarizedcall a single implementation parameterized by the backend, without altering report names,TestStatus.result!payloads, ortools/ci/scope.pyscoping rules.
- Vorherrschende Sprache
- Julia
- Sterne
- 13
- Forks
- 1
- Ø Merge
- 3 Std. 36 Min.
- Gemergte PRs (30 T.)
- 57
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Keine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus control-toolbox/OptimalControlProblems.jl
-
Share the GPU smoke test between OptimalControl and OptimalControlScalarizedEvtl. vergeben @ocots hat das heute übernommen. Offen
control-toolbox/OptimalControlProblems.jl#445 · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 1 Tag
-
[Cron/Doc] ModelingToolkit convergence cron, trajectories, and documentation (#308)Evtl. vergeben @ocots hat das vor 2 Tagen übernommen. Offendocumentation feature
control-toolbox/OptimalControlProblems.jl#439 · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 1 Tag
-
cron-convergence
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 32/100
control-toolbox/OptimalControlProblems.jl#436 · 3 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
[Cron] cron-failureOffencron-failure
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 35/100
control-toolbox/OptimalControlProblems.jl#435 · 5 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
[Dev] Expose the duals of the JuMP path, boundary and box constraints, aligned with signs.mdEvtl. vergeben @ocots hat das vor 2 Tagen übernommen. Offen
control-toolbox/OptimalControlProblems.jl#430 · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in control-toolbox/OptimalControlProblems.jl
Ähnliche Issues
-
documentation priority: high
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 90/100
epiforecasts/BVDOutbreakSize#1260 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
TuringLang/Turing.jl#2917 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 88/100
-
Validate UserDemand return_factorEvtl. vergeben Ein verknüpfter Pull Request ist offen oder bereits gemergt. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 65/100
Maintainer antworten meist innerhalb von 2 Tagen