Skip to content

ExaModels 0.12 exports objective and constraint, clashing with ours — and the docs tell users to import it #882

Description

@ocots

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

  1. 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.
  2. Where a page genuinely needs ExaModels symbols, use using ExaModels: ExaModels and say
    why in prose, matching CTParser#230.
  3. 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.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentation

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions