Skip to main content

Module unimplemented_options

Module unimplemented_options 

Source
Expand description

Options registered for ipopt.opt compatibility whose feature pounce does not implement (gh#483 follow-up, continuing #191).

§Why these are refused rather than ignored

upstream_options.rs is a faithful port of Ipopt’s option registry: every name Ipopt registers is registered here, so an ipopt.opt written for Ipopt parses unchanged. That is a real compatibility benefit — and it silently turned ~200 knobs into no-ops, because registering an option says nothing about implementing it. Setting one did exactly nothing and said exactly nothing.

Issue #191 audited this class and fixed the half where the feature runs and only the option’s read site was missing. It explicitly scoped out “feature genuinely unimplemented — expected no-ops”. This module closes that half: an option naming a feature pounce does not have is now an error, not a shrug.

§What is and is not in the table

Membership was established per option, not guessed:

  1. the option’s name appears in no crate source outside the registry (whole-word — penalty_max does not count as present because l1_penalty_max exists), and
  2. the feature it configures is absent too.

Both are needed. An option whose name is unread but whose feature runs — the limited_memory_* tail — is a missing read site, not a missing feature; refusing those would fail solves whose current answers are already correct. They are deliberately not here; wiring them is the other half of the work.

Rule 2 is the one that takes work, and the corrector knobs are why. corrector_type and its three safeguards were once listed here as an example of rule 1 alone — “the Mehrotra corrector runs, so these need read sites” — on the strength of pounce having a corrector at all. They configure a different one. The registry files them under FilterLSAcceptor::RegisterOptions, and what they select is FilterLSAcceptor::TryCorrector: a corrector step tried inside the line search and accepted only if complementarity does not grow by more than corrector_compl_avrg_red_fact. pounce’s corrector is Mehrotra’s, in the search-direction right-hand side, reached through mehrotra_algorithm — an option upstream registers separately and pounce reads. No acceptor here takes a corrector trial, so the four are refused (#551 / #677). A dev-note had already measured the consequence without naming it: corrector_type=affine on robot_a was recorded as “identical to plain adaptive”.

A third shape belongs to neither rule: an option for a sub-capability of a feature that does run. resto_failure_feasibility_threshold is the example — restoration runs, but nothing reclassifies a stopped restoration as a failure below a feasibility threshold, so honouring it means building the capability, not adding a line. Those entries are refused too, and their advice says what the parent feature does instead, so the message tells a user which of the two they hit. max_resto_iter looked like this shape and was not: RestoConvCheckAdapter has capped successive restoration iterations all along, under the name maximum_resto_iters. It is wired, not refused.

The clearest case is the penalty line search. pounce implements IpPenaltyLSAcceptor (line_search_method=penalty), so its knobs (nu_init, nu_inc, rho, eta_penalty) are read sites to add. Ipopt’s other penalty acceptor — the CG-penalty / inexact-Newton one — has no counterpart here at all, and the port registered its whole option set. Those are refused.

An entry leaves the table by being implemented. option_file_name was here — refusing it was the cheap half of gh#518’s “implement it or fail loudly” — until gh#518 got the other half: crate::application::IpoptApplication::initialize_with_option_file reads the named file, so the option now configures something.

§Backend knobs warn, they do not refuse

The 111 ma27_* / ma77_* / ma86_* / ma97_* / mumps_* / pardiso_* / pardisomkl_* / spral_* / wsmp_* options, plus pardisolib, tune linear-solver backends pounce does not ship at all: it factors the KKT system with feral or with MA57. They were silent — #551 section 2 — and they are the one class where the refusal above is the wrong instrument.

Refusing them attacks the goal the registry exists to serve. The rest of this table refuses an option whose feature the caller is plainly asking for; ma97_order in a portable ipopt.opt is usually not that. Such a file routinely carries settings for several backends at once so that one file runs everywhere, and pounce would reject it wholesale over knobs the run never touches — a hard error for a user who is not using MA97 and never asked pounce to. That is strictly worse than the silence: it breaks a working file instead of under-serving one.

But silence is what #677 cost, so the answer is the third disposition this module already carries. The precedent is UNEXPLOITED_HINTS — pinned by a_caching_hint_warns_but_solves in pounce-cli/tests/unimplemented_options.rs — where the project chose WARN over REFUSE for exactly this trade: ignoring the option costs the caller nothing they cannot see, so blocking the solve would take more from them than the silence did. Backend knobs are a stronger case for it than the hints are: a hint changes the evaluation count, whereas a knob for a backend that is not linked could not have changed anything even in principle.

Three properties follow from that, and each is pinned by a test:

  1. Only when explicitly set to a non-default. The same gate as everything else here (below). A file spelling out ma97_u 1e-8 asks for nothing, and a default run must stay completely silent — a_default_run_is_silent.
  2. One line per backend family, not per option. An MA97-tuned file sets a dozen ma97_* knobs; a dozen near-identical lines is noise a reader learns to skip, which is silence with extra steps. The warning names the backend, lists the options it saw, and says the rest of the family is inert too.
  3. The solve runs and its answer is unaffected, which the warning says in as many words — otherwise a warning naming a linear solver reads as “your factorization may be wrong”.

hsllib stays in the refusal table above rather than moving here, and the line is deliberate: pounce has an HSL backend (MA57), so hsllib is a caller trying to reach a solver pounce can actually run, by a mechanism it does not have — the refusal tells them to build with --features ma57 instead of letting them believe MA57 is loaded. pardisolib has no such other route (there is no Pardiso here by any means), so it warns with the rest of its family.

§…unless they are all there is

The rule above rests on one premise, stated in it: the file has other business here, and failing the run over ma97_order would reject something the caller wanted for the sake of a knob it never touches. When the backend knobs are all that is there, the premise is gone. Nothing in the file survives, so there is no working run left to protect, and warning-then-solving answers “tune the linear solver” by tuning nothing and reporting success — the shape of gh#677, not a fix for it.

So backend_only_refusal refuses that one case. It is the boundary of the warn rule rather than an exception to it: every file the portability argument was ever about still warns and still solves, because every such file has something else in it.

Note that a file which selects the backend it tunes never reaches here — linear_solver=ma97 is refused earlier, by crate::application::IpoptApplication::unimplemented_linear_solver. What this catches is the file that tunes MA97 without ever saying so, which is the case that used to run FERAL and report success.

§The default gate

Only an explicit value different from the registered default is refused. corrector_type left alone, or an ipopt.opt that spells out defaults, must keep working: those ask for nothing. Refusing them would break the very compatibility the registry exists to provide. This gate binds backend_only_refusal too: a file of nothing but backend knobs at their registered defaults is silent, not refused.

Structs§

UnimplementedBackend
One linear-solver backend pounce does not implement, and the registered options that tune it.
UnimplementedFeature
One unimplemented feature and the options that configure it.
UnimplementedValue
One registered value of a string option that pounce does not implement, even though the option itself is read and other values of it work.

Constants§

CONVEX_UNEXPLOITED_HINTS
The same four hints again — on the convex route, where they are still unexploited.
UNEXPLOITED_HINTS
Options that are honored in the sense that matters — the answer is unaffected — but whose performance hint pounce does not exploit. These warn rather than fail: refusing them would stop a solve that returns the right result today, only a little slower.
UNIMPLEMENTED_BACKENDS
Every linear-solver backend pounce does not implement, with the options that tune it. Warned about when set; never refused.
UNIMPLEMENTED_FEATURES
Feature groups pounce does not implement. Refused when set.
UNIMPLEMENTED_VALUES
String-option values pounce does not implement. Refused when set.

Functions§

backend_only_refusal
The refusal for a run whose options configure nothing but backends pounce does not ship, or None.
backend_warnings
Warnings for backend knobs the caller set. Never blocks a solve, and emits at most one line per backend family: an ipopt.opt tuned for MA97 sets a dozen ma97_* knobs at once, and a dozen near-identical lines would be noise the reader learns to skip.
convex_hint_warnings
Warnings for the constant-derivative hints on the convex route, where — unlike the NLP route — nothing reads them. Never blocks a solve. Call this only when the convex dispatch is actually taken; on the NLP route the same options are honoured, and warning there would contradict the reuse the solver really does.
hint_warnings
Warnings for hints pounce does not exploit. Never blocks a solve.
refusal
The first unimplemented-feature option the caller set, with the message it earns. None when nothing in the table was touched.
value_refusal
The first unimplemented value the caller selected, with the message it earns. None when every string option holds a mode pounce runs.