Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Solution Output

The .sol file

Following the AMPL solver convention, solving a positional .nl file writes a sibling <stub>.sol next to it — pounce problem.nl produces problem.sol. The file carries the primal x and dual lambda blocks plus an objno line with the AMPL solve_result_num, so AMPL (or any .sol reader) can pull the solution back:

pounce problem.nl                       # writes problem.sol
pounce problem.nl --sol-output out.sol  # write to an explicit path
pounce problem.nl --no-sol              # skip the .sol write

A .sol is written even when the solve fails, so the solve_result_num is always recoverable. Built-in problems (--problem …) have no .nl stub, so they only produce a .sol when --sol-output is given explicitly.

Reading solve_result_num

The objno line carries an AMPL solve_result_num (Gay 2005, Hooking Your Solver to AMPL §5). Consumers key on the band, not the exact number:

BandMeaning
099solved
100199solved, with a warning
200299infeasible
300399unbounded
400499limit reached (iterations, time)
500599failure

Pyomo maps each band to a TerminationCondition, so anything in 200299 arrives as TerminationCondition.infeasible.

Infeasible: proved vs. local

Within the infeasible band POUNCE distinguishes how it knows:

CodeVerdictWhat it means
200InfeasibleProblemDetectedThe solver converged to a point of local infeasibility — a stationary point of the constraint violation with the violation bounded away from zero.
201... (detected by presolve: …)Presolve’s bound propagation / interval arithmetic found the feasible region empty before any iteration.

The difference is real, not cosmetic. 201 is a structural detection made on the model’s bounds before iterating, not a certified proof — it is subject to the same floating-point limits as any interval computation, and is withheld whenever the violation is smaller than the feasibility tolerance. 200 is different in kind — on a nonconvex problem a positive local minimum of the violation does not rule out a feasible point elsewhere, which is why the console message says “Problem may be infeasible.”

Because 200 is an inference rather than a proof, it is withdrawn when POUNCE holds a point that contradicts it. Before any numerical path reports 200, the model’s own starting point is evaluated against every constraint; if it satisfies them all, the feasible set is demonstrably non-empty and the verdict becomes Error_In_Step_Computation (500) — an honest “the solve broke down” rather than a wrong answer. This can only ever withdraw a verdict: a model with no feasible point cannot produce such a point, so a correct 200 is unaffected. Supplying a feasible starting point is therefore worth doing on a model you believe is feasible but POUNCE reports otherwise.

When the region is found empty the solve is skipped entirely and the message names how it was found, so the claim is checkable:

POUNCE 0.9.0: InfeasibleProblemDetected (detected by presolve: bound propagation)
objno 0 201

201 requires presolve to be enabled (presolve=yes); it is off by default. A presolve-derived infeasibility is only reported when the contradiction holds on the original box — one produced by presolve’s own auxiliary elimination is re-checked after rollback and never certified.

One more route to 200: over-determined systems

An over-determined model — more equality rows than free variables, such as x == 0.2 with x == 0.8 — cannot be solved at all: it fails a structural gate before the first iteration. That used to be reported as Not_Enough_Degrees_Of_Freedom (504, the failure band), which says “cannot attempt this” for a model whose answer is already decided.

POUNCE now checks such a model for a bound-propagation contradiction on that failure path and reports 200 when it finds one. This does not need presolve=yes — nothing is transformed and no solve runs through the check — so it is the one way to reach the infeasible band with the default options and no iterations. A consistent over-determined system is unaffected and still reports 504.

Because the solve provably cannot run here, this route measures constraint residuals against each row’s declared magnitude rather than an absolute tolerance, so the verdict does not change when every row is multiplied by a constant. Elsewhere — wherever a solve can run — an infeasibility smaller than the feasibility tolerance is still withheld, as described above.

Choosing an output format

You want…Use
AMPL / Pyomo to read the result backthe .sol file (default)
A structured, schema-versioned report for tooling--json-output (see JSON Solve Report)
Just the console summary--no-sol

The .sol and JSON outputs are not exclusive — you can request both in the same run.