Summary
The src/mps_parser.c file mishandles the OBJSENSE section in two independent ways. Together, they cause maximization instances to be solved incorrectly and silently (returning OPTIMAL status with no warnings).
Specifically:
- The
OBJSENSE value is dropped, causing the problem to be solved as a minimization (resulting in a wrong optimum).
- Even if (1) is fixed, the reported objective value retains the wrong sign.
Bug 1: OBJSENSE MAXIMIZE is swallowed (Wrong Optimum)
Section headers are currently detected by treating any single alphabetic token as a header:
if (n_tokens == 1 && isalpha(tokens[0][0]))
{
MpsSection next_section = SEC_NONE;
if (strcmp(tokens[0], "ROWS") == 0) next_section = SEC_ROWS;
/* ... */
else if (strcmp(tokens[0], "OBJSENSE") == 0) next_section = SEC_OBJSENSE;
/* ... */
current_section = next_section; /* unconditional */
if (current_section == SEC_ENDATA) break;
continue; /* line consumed */
}
With the standard two-line form:
The value line MAXIMIZE is parsed as a single alphabetic token. It enters the section header block, matches no keywords (so next_section stays SEC_NONE), unconditionally sets current_section = SEC_NONE, and skips to the next line. The case SEC_OBJSENSE: block that should set is_maximize is never reached. As a result, is_maximize defaults to false, the objective is not negated, and the problem is minimized instead.
Reproduction
Consider the following problem: maximize x s.t. x <= 5, x >= 0 (True optimum x* = 5, objective 5):
NAME maxtest
OBJSENSE
MAXIMIZE
ROWS
N obj
L c1
COLUMNS
x obj 1.0 c1 1.0
RHS
rhs c1 5.0
BOUNDS
ENDATA
- Expected:
OPTIMAL, x ~= 5, Objective = 5
- Actual:
OPTIMAL, x ~= 0, Objective = 0 (The problem was treated as a minimization)
Bug 2: Objective sign is not restored for maximization (Wrong Reported Value)
When is_maximize is successfully parsed, the parser converts max → min by negating the objective:
prob->objective_constant = state.is_maximize ? -state.objective_constant
: state.objective_constant;
/* ... */
if (state.is_maximize)
for (int i = 0; i < prob->num_variables; ++i)
prob->objective_vector[i] *= -1.0;
However, is_maximize is strictly a local parser variable—it is not stored on the returned lp_problem_t struct, and nothing ever negates the reported objective back at the end of the solve. Consequently, the solver reports min(-c·x) = -(max value).
If Bug 1 is fixed, running the reproduction above yields the correct x* = 5 but reports an objective of -5 instead of +5 (since cupdlpx.c contains no objective-sense restoration logic).
Suggested Fixes
- For Bug 1: Restrict the section header detection so that it does not falsely match single-token values inside the
OBJSENSE section, or explicitly check for valid section keywords before overwriting current_section.
- For Bug 2: Add an
is_maximize flag (or an obj_sense variable) to the lp_problem_t struct to store the original problem sense. Use this flag in the solver output step to negate the final objective value back to the correct sign before reporting.
Summary
The
src/mps_parser.cfile mishandles theOBJSENSEsection in two independent ways. Together, they cause maximization instances to be solved incorrectly and silently (returningOPTIMALstatus with no warnings).Specifically:
OBJSENSEvalue is dropped, causing the problem to be solved as a minimization (resulting in a wrong optimum).Bug 1:
OBJSENSE MAXIMIZEis swallowed (Wrong Optimum)Section headers are currently detected by treating any single alphabetic token as a header:
With the standard two-line form:
The value line
MAXIMIZEis parsed as a single alphabetic token. It enters the section header block, matches no keywords (sonext_sectionstaysSEC_NONE), unconditionally setscurrent_section = SEC_NONE, and skips to the next line. Thecase SEC_OBJSENSE:block that should setis_maximizeis never reached. As a result,is_maximizedefaults tofalse, the objective is not negated, and the problem is minimized instead.Reproduction
Consider the following problem:
maximize x s.t. x <= 5, x >= 0(True optimumx* = 5, objective5):OPTIMAL,x ~= 5, Objective= 5OPTIMAL,x ~= 0, Objective= 0(The problem was treated as a minimization)Bug 2: Objective sign is not restored for maximization (Wrong Reported Value)
When
is_maximizeis successfully parsed, the parser converts max → min by negating the objective:However,
is_maximizeis strictly a local parser variable—it is not stored on the returnedlp_problem_tstruct, and nothing ever negates the reported objective back at the end of the solve. Consequently, the solver reportsmin(-c·x) = -(max value).If Bug 1 is fixed, running the reproduction above yields the correct
x* = 5but reports an objective of-5instead of+5(sincecupdlpx.ccontains no objective-sense restoration logic).Suggested Fixes
OBJSENSEsection, or explicitly check for valid section keywords before overwritingcurrent_section.is_maximizeflag (or anobj_sensevariable) to thelp_problem_tstruct to store the original problem sense. Use this flag in the solver output step to negate the final objective value back to the correct sign before reporting.