Summary
ExaModels 0.12 newly exports objective and constraint. Both collide with
OptimalControl's accessors of the same name. Two documentation pages tell the reader to write
a bare using ExaModels, so following the documentation produces an UndefVarError on the
next objective(sol) — and the documentation build cannot catch it.
Found while raising the compat bounds to CTBase 0.29 / CTModels 0.18 / CTFlows 0.17 /
CTParser 0.9 / CTDirect 1.1 / ExaModels 0.12.
Reproduction
using OptimalControl
using ExaModels # exactly what docs/src/solve/gpu.md:24 says to write
using NLPModelsIpopt
ocp = @def begin
t ∈ [0, 1], time
x = (q, v) ∈ R², state
u ∈ R, control
x(0) == [-1, 0]
x(1) == [0, 0]
∂(q)(t) == v(t)
∂(v)(t) == u(t)
∫(0.5u(t)^2) → min
end
sol = solve(ocp, :exa; display=false)
objective(sol)
ERROR: UndefVarError: `objective` not defined in `Main`
Hint: It looks like two or more modules export different bindings with this name,
resulting in ambiguity.
Hint: a global variable of this name also exists in CTModels.Components.
- Also exported by OptimalControl.
Hint: a global variable of this name also exists in ExaModels.
Cause
The clash is new in 0.12. Comparing export lists:
|
objective |
constraint |
add_obj / add_con / add_var |
| ExaModels 0.11.2 |
not exported |
not exported |
exported |
| ExaModels 0.12.0 |
exported |
exported |
exported |
In 0.12 the names come back as oracle builders — objective(c::ExaCore, oracle::ScalarNonlinearOracle) (src/oracle.jl:261) and constraint(c::ExaCore, oracle::VectorNonlinearOracle) (src/oracle.jl:338) — and are added to the export block at
src/ExaModels.jl:98-99.
Measured against the resolved environment:
julia> intersect(names(ExaModels), names(OptimalControl))
2-element Vector{Symbol}:
:constraint
:objective
This is the same class of clash CTParser hit on constraint
(CTParser.jl#230), fixed there
with a qualified using ExaModels: ExaModels. Nothing has fixed it on the user side.
Why the documentation build will not catch it
Neither page that writes the bare using calls objective afterwards:
| page |
bare using ExaModels |
calls objective( |
docs/src/solve/gpu.md:24 |
yes |
no |
docs/src/getting-started/installation.md:112 |
yes |
no |
gpu.md is Draft = true on top of that, so it is not executed at all. The reader who
copies the prerequisites and then moves on to
Solution object,
which does call objective(sol), is the one who hits it.
The using is not even needed
src/imports/examodels.jl already does @reexport import ExaModels: ExaModels, and ExaModels
is a hard dependency in [deps]. So the module is loaded and bound by using OptimalControl
alone. Verified — :exa solves with no using ExaModels anywhere:
using OptimalControl
using NLPModelsIpopt
sol = solve(ocp, :exa; display=false) # successful = true, objective = 6.0000960
Both pages ask the reader to import a package they already have, and the import is what breaks
them.
Proposed fix
- Drop the bare
using ExaModels from docs/src/solve/gpu.md and
docs/src/getting-started/installation.md; state that :exa needs no extra import.
- Where a page genuinely needs ExaModels symbols, use
using ExaModels: ExaModels and say
why in prose, matching CTParser#230.
- Consider a regression test asserting
isempty(intersect(names(ExaModels), names(OptimalControl))), or an explicit allow-list, so
the next upstream export lands as a red test rather than as a user's bug report.
Open question
Should this also go upstream? Exporting objective/constraint is ExaModels' prerogative, but
these are exceptionally generic names for a modelling package to claim, and the collision is
guaranteed against any optimisation package with accessors. Happy to open one at
exanauts/ExaModels.jl if wanted.
Summary
ExaModels 0.12 newly exports
objectiveandconstraint. Both collide withOptimalControl's accessors of the same name. Two documentation pages tell the reader to write
a bare
using ExaModels, so following the documentation produces anUndefVarErroron thenext
objective(sol)— and the documentation build cannot catch it.Found while raising the compat bounds to CTBase 0.29 / CTModels 0.18 / CTFlows 0.17 /
CTParser 0.9 / CTDirect 1.1 / ExaModels 0.12.
Reproduction
Cause
The clash is new in 0.12. Comparing export lists:
objectiveconstraintadd_obj/add_con/add_varIn 0.12 the names come back as oracle builders —
objective(c::ExaCore, oracle::ScalarNonlinearOracle)(src/oracle.jl:261) andconstraint(c::ExaCore, oracle::VectorNonlinearOracle)(src/oracle.jl:338) — and are added to the export block atsrc/ExaModels.jl:98-99.Measured against the resolved environment:
This is the same class of clash CTParser hit on
constraint(CTParser.jl#230), fixed there
with a qualified
using ExaModels: ExaModels. Nothing has fixed it on the user side.Why the documentation build will not catch it
Neither page that writes the bare
usingcallsobjectiveafterwards:using ExaModelsobjective(docs/src/solve/gpu.md:24docs/src/getting-started/installation.md:112gpu.mdisDraft = trueon top of that, so it is not executed at all. The reader whocopies the prerequisites and then moves on to
Solution object,
which does call
objective(sol), is the one who hits it.The
usingis not even neededsrc/imports/examodels.jlalready does@reexport import ExaModels: ExaModels, and ExaModelsis a hard dependency in
[deps]. So the module is loaded and bound byusing OptimalControlalone. Verified —
:exasolves with nousing ExaModelsanywhere:Both pages ask the reader to import a package they already have, and the import is what breaks
them.
Proposed fix
using ExaModelsfromdocs/src/solve/gpu.mdanddocs/src/getting-started/installation.md; state that:exaneeds no extra import.using ExaModels: ExaModelsand saywhy in prose, matching CTParser#230.
isempty(intersect(names(ExaModels), names(OptimalControl))), or an explicit allow-list, sothe next upstream export lands as a red test rather than as a user's bug report.
Open question
Should this also go upstream? Exporting
objective/constraintis ExaModels' prerogative, butthese are exceptionally generic names for a modelling package to claim, and the collision is
guaranteed against any optimisation package with accessors. Happy to open one at
exanauts/ExaModels.jl if wanted.