Skip to content

AmbiguousDescription: Available now lists the closest matches instead of what is available, and the Hint still promises a list it never prints #557

Description

@ocots

Follow-up to #553, fixed by #556 and released in v0.30.3-beta. The original harm is gone — the GPU methods are no longer hidden — but the fix routed the closest matches into the field the display labels Available, so the two labels have in effect swapped, and the Hint line still ends on a colon with nothing after it.

Reported from OptimalControl.jl's solve/gpu.md, whose executed output publishes this message on the documentation site.

What it prints now (CTBase v0.30.3-beta)

julia> solve(ocp, :adnlp, :gpu)
AmbiguousDescription → _complete_description, strategy_builders.jl:260
│
│  cannot find matching description
│
│  Diagnostic  No complete match — no description contains all symbols
│  Requested   (:adnlp, :gpu)
│  Available   (:collocation, :exa, :madnlp, :gpu)
│              (:collocation, :exa, :madncl, :gpu)
│              (:collocation, :adnlp, :uno, :cpu)
│              (:collocation, :adnlp, :madnlp, :cpu)
│              (:collocation, :adnlp, :madncl, :cpu)
│
│  Context     description completion
│  Hint        Try one of the closest matches:
└─

Two things are wrong with that, and they are the same bug seen from both ends:

  • Available lists 5 of the 12 available descriptions, chosen by similarity, with nothing saying so. A reader asking "what can I actually call?" is given a filtered subset under a label that claims completeness.
  • Hint announces a list and prints none. The closest matches are on screen — above, under the wrong heading.

Why, reading the code

The display labels are fixed. src/Exceptions/display.jl:157-159 hardcodes Available for candidates:

if !isnothing(e.candidates) && !isempty(e.candidates)
    push!(pairs, ("Available", e.candidates, :default))
end

and display.jl:223-225 renders Hint from suggestion. Neither knows what the caller put in the field.

src/Descriptions/complete.jl:96 now decides between two different kinds of content for that one field:

candidates=isempty(similar_descs) ? all_candidates : similar_descs,

so whenever similar_descs is non-empty — the common case — the Available block holds closest matches, and the Hint at complete.jl:78 is left dangling.

A second consequence: #556's truncation marker is unreachable in practice

all_candidates is computed at complete.jl:74 with the new max_show=20, and _format_description_candidates appends … and N more when the catalog is longer. But that value is now discarded whenever similar_descs is non-empty, and _find_similar_descriptions returns empty only when no description shares a single symbol with the request — _compute_similarity is a Jaccard index and the results are filtered with filter(x -> x[1] > 0.0, similarities). That is exactly the "unknown symbols" diagnostic.

So the marker introduced by #556 can only ever render when both conditions hold: the catalog has more than 20 entries and the request shares no symbol with any of them. In the case #553 was filed about, it never appears.

Verified on v0.30.3-beta with OptimalControl's 12-method registry and Requested = (:adnlp, :gpu):

similar_descs empty? false     all_candidates count: 12     marker present? false

Suggested fix — complete.jl only, no change to display.jl

_print_pipe_field already splits a String value on newlines and keeps the │ prefix and label indentation on continuation lines (display.jl:283-291), so a multi-line suggestion renders correctly today. That makes the fix two lines:

@@ src/Descriptions/complete.jl:77
         suggestion = if !isempty(similar_descs)
-            "Try one of the closest matches:"
+            string("Try one of the closest matches:\n", join(similar_descs, "\n"))
         elseif !isempty(all_candidates)
             "Choose from the available descriptions listed above"
         else
             "Check your input symbols and available descriptions"
         end
@@ src/Descriptions/complete.jl:96
-                candidates=isempty(similar_descs) ? all_candidates : similar_descs,
+                candidates=all_candidates,

Available goes back to meaning available, the hint carries its own list, and all_candidates — with its max_show=20 and its marker — is used on every path instead of being thrown away.

Rendered with that patch applied (real output, not a mock-up):

│  Diagnostic  No complete match — no description contains all symbols
│  Requested   (:adnlp, :gpu)
│  Available   (:collocation, :adnlp, :ipopt, :cpu)
│              (:collocation, :adnlp, :madnlp, :cpu)
│              (:collocation, :adnlp, :uno, :cpu)
│              (:collocation, :adnlp, :madncl, :cpu)
│              (:collocation, :adnlp, :knitro, :cpu)
│              (:collocation, :exa, :ipopt, :cpu)
│              (:collocation, :exa, :madnlp, :cpu)
│              (:collocation, :exa, :uno, :cpu)
│              (:collocation, :exa, :madncl, :cpu)
│              (:collocation, :exa, :knitro, :cpu)
│              (:collocation, :exa, :madnlp, :gpu)
│              (:collocation, :exa, :madncl, :gpu)
│
│  Context     description completion
│  Hint        Try one of the closest matches:
│              (:collocation, :exa, :madnlp, :gpu)
│              (:collocation, :exa, :madncl, :gpu)
│              (:collocation, :adnlp, :uno, :cpu)
│              (:collocation, :adnlp, :madnlp, :cpu)
│              (:collocation, :adnlp, :madncl, :cpu)
└─

If you would rather the two lists be structurally distinct rather than one of them living inside a hint string, the alternative is a closest::Union{Nothing,Vector{String}} keyword on AmbiguousDescription plus a Closest entry in _build_primary_pairs. Non-breaking (defaulted keyword), but it touches types.jl and display.jl as well; the two-line version above needs neither.

Reproduction

using CTBase
const D, E = CTBase.Descriptions, CTBase.Exceptions

catalog = ((:collocation,:adnlp,:ipopt,:cpu), (:collocation,:adnlp,:madnlp,:cpu),
           (:collocation,:adnlp,:uno,:cpu),   (:collocation,:adnlp,:madncl,:cpu),
           (:collocation,:adnlp,:knitro,:cpu),(:collocation,:exa,:ipopt,:cpu),
           (:collocation,:exa,:madnlp,:cpu),  (:collocation,:exa,:uno,:cpu),
           (:collocation,:exa,:madncl,:cpu),  (:collocation,:exa,:knitro,:cpu),
           (:collocation,:exa,:madnlp,:gpu),  (:collocation,:exa,:madncl,:gpu))

D.complete(:adnlp, :gpu; descriptions=catalog)   # Available = 5 closest, Hint empty

Display-only, as before; complete's resolution behaviour is not involved.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions