Skip to content

feat: references say how they were obtained, as typed values (#409, part 2) - #423

Open
ocots wants to merge 4 commits into
chore/413-src-submodulesfrom
feat/409-typed-references
Open

ocots wants to merge 4 commits into
chore/413-src-submodulesfrom
feat/409-typed-references

Conversation

@ocots

@ocots ocots commented Nov 7, 2026

Copy link
Copy Markdown
Member

Closes #409 (part 2 of 2; part 1 was #412). Stacked on #421 (#413, split of src): the base of this PR is chore/413-src-submodules; once #421 is merged, the base becomes main. Conventions: ct-conventions v0.10.0 (TYP4, TYP7, TST3, MOD6), ct-agents v0.4.0.

Plan

Goal. Every known solution says how it was obtained, as data generated from the cron results (never by hand), and the package exposes the references as typed values (#409, decisions of 2026-11-07: never by hand, no trajectories, acceptable solves allowed, analytic references separate).

Steps.

  1. tools/references/references.jl: read <report>-environment.toml next to each results file; refuse results without the columns of test: record how each solve was configured and the environment of the run (#409, part 1) #412; choose the representative of each solution (strict before acceptable, then smallest violation, then smallest objective, then a stable tie-break); write its table reference (modeling, configuration, modeler, solver, options, status, iterations, time, violation, platform, run, commit, Julia, OS, architecture, CPU, package versions). value and each solution's objective are those of the representative: a solve that can be redone.
  2. src/References/types.jl: AbstractReference with the contract objective(ref) (TYP4: NotImplemented on the abstract type), NumericalReference, AnalyticReference (TYP7: different data).
  3. src/References/objectives.jl: the loader builds the NumericalReferences of each scheme (field references); API references(p; grid_size, scheme), best_reference, analytic_reference(p, parameters); exported by References, reached as OptimalControlProblems.references(…) (the package exports are unchanged, [Dev] Split src into submodules (ct-conventions MOD1, MOD2), keeping the public exports #413).
  4. The 22 reference files regenerated from the four crons dispatched on main after test: record how each solve was configured and the environment of the run (#409, part 1) #412 (run ids in [Metadata] Reference solutions with how they were obtained: typed references and API #409).
  5. Tests: the tool (representative, reference table, refusals), the loader, the API on every problem, the consistency of the files; each shown failing on a broken case.
  6. tools/references/README.md, src/README.md.

Done when (#409)

Evidence

Regeneration:

julia --project=. tools/references/update.jl --run "<4 run urls>" runs/42387105573 runs/42387108959 runs/42387112175 runs/42387115100
bioreactor trapeze: -12.929690242620518 (spread 0.000328, 26 solves)  <-- 5 solutions
brachistochrone trapeze: 1.802932061276909 (spread 0.0225, 27 solves)  <-- 2 solutions
moonlander trapeze: 0.9618109334441546 (spread 3.51e-5, 22 solves)  <-- 2 solutions
mountain_car trapeze: 102.3809949692475 (spread 0.000162, 29 solves)  <-- 2 solutions
…
629 results read, 22 files written.

The API:

julia> ref = OptimalControlProblems.best_reference(:moonlander)
Known solution of moonlander, trapeze on 500 steps
  objective:  0.9618109334441546 (21 solves)
  solved by:  JuMP, ipopt, LOCALLY_SOLVED
  iterations: 66, violation 3.552713678800501e-15
  run:        https://github.com/control-toolbox/OptimalControlProblems.jl/actions/runs/42387115100 (cron-JuMP-robustness-1.13-windows-latest)

julia> ref.options
(linear_solver = "mumps", max_iter = 1000, max_wall_time = 500.0, mu_init = 0.001, mu_strategy = "monotone", sb = "yes", tol = 1.0e-8)

julia> ref.versions.Ipopt_jll
"300.1400.1902+0"

The second known solution of moonlander (0.961844, the case of #367) is the acceptable solve, recorded with status = "acceptable".

Tests (Pkg.test(test_args=["core"]), macOS, Julia 1.12.7): core | 1249 pass, 1 broken (existing); reference values | 161, reference objectives green.

The checks fail on broken cases.

  • A reference file written before [Metadata] Reference solutions with how they were obtained: typed references and API #409 (beam.toml from main):
    reference objectives: Test Failed at test/suite/core/test_consistency.jl:114
      Expression: length(entry.references) == length(entry.solutions)
    
  • The representative chosen without the strict-before-acceptable rule (the fixture gives the acceptable solve the smallest objective and violation, so that only this rule excludes it):
      Expression: alpha["value"] == 2.0000000001
       Evaluated: 1.99999999 == 2.0000000001
      Expression: reference["modeling"] == "G"
       Evaluated: "A" == "G"
    

Restored: all green.

Choices

  1. The value is the representative's objective, not the minimum of the solves (they differ by less than SAME_SOLUTION): the reference is then a solve that exists and can be redone. Reference objectives move accordingly within 1e-5 relative (e.g. beam 8.88928162 → 8.88928181).
  2. Representative: strict before acceptable, then the smallest violation. The violation is what each modeling measures; JuMP counts the variable bounds that Ipopt relaxes, so among strict solves an OptimalControl one is usually preferred. Documented; not a correctness issue, since every solve of a solution reaches it.
  3. A pre-[Metadata] Reference solutions with how they were obtained: typed references and API #409 reference file still loads, with no references: update.jl loads the package, so a loader that refused such files would block their regeneration. The consistency test fails on it instead.
  4. objective(ref) extends CTModels.Components.objective, the function the package already exports as objective: one function for solutions and references, no name clash with OptimalControl. Not piracy: the methods are for our types.
  5. options and versions are NamedTuples with sorted keys (the TOML tables are unordered), so equal tables give equal values.
  6. No trajectories (decision of [Metadata] Reference solutions with how they were obtained: typed references and API #409): the configuration is enough to solve again; trajectories and plots belong to [Doc] Results pipeline for the documentation, and a gallery of problems #314.
  7. Out of scope, to propose: showing how the reference was obtained on the problem pages of the documentation ([Doc] Results pipeline for the documentation, and a gallery of problems #314 / [Doc] Rewrite the documentation: getting started, user guide, problems, developers #307), and the machine-level fields there.

Labels

run ci github-runner (scope: every reference file changed, so every environment), run documentation (the problem pages read the reference files).

🤖 Generated with Claude Code

ocots and others added 2 commits November 7, 2026 22:58
tools/references: each known solution keeps the solve that represents it (strict before
acceptable, then the smallest violation, then the smallest objective) with its modeling,
modeler, solver and every option, status, iterations, time, violation, and the environment
of its run. The results must hold the columns and the environment file of #412.

References: AbstractReference, NumericalReference (a known solution and how it was
obtained), AnalyticReference (the solution of the continuous problem); references,
best_reference, analytic_reference, and objective(ref). A reference file written before
#409 still loads, without references, so that the tool can write it again; the consistency
tests fail on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ocots ocots added run ci github-runner Trigger the CI workflow on GitHub-hosted runners run documentation Trigger the Documentation workflow on this PR labels Nov 7, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ocots
ocots marked this pull request as ready for review November 7, 2026 22:20
Conflict in src/README.md: both sides extend the submodule table
(typed references in References, the problem fingerprint in Catalogue).

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 run documentation Trigger the Documentation workflow on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant