perf(solvers): stop setting names in direct solver APIs by default - #988
Conversation
Merging this PR will improve performance by 50.68%
|
Build cost — v1 vs legacyv1 build peak & time relative to legacy, on this commit — not a comparison against master (that is CodSpeed).
Full table (time + peak, mean)📊 Interactive plots + CSV: download the semantics-report-v1-vs-legacy artifact from this run. Report-only · not a gate · refreshed on every push · obsolete once legacy is dropped. |
Closes #978.
Note
The following content was generated by AI.
Changes proposed in this Pull Request
Setting the default names
x{label}/c{label}was the largest single cost of the direct solver APIs. On a model with 200k variables and 200k constraints,to_highspytook 136 ms with names and 69 ms without. The names carry no information beyond the index, and the solution is already mapped back by position.Why not the fix suggested in the issue: building a
highspy.HighsLpin one pass is slower than the currentgetLp/passModelround trip. highspy copies numpy arrays intostd::vectorelement by element (about 55 ms for 600k nonzeros, against about 25 ms for the round trip). There is no bulk name setter (apassColNameloop takes 168 ms), and polars is already the fastest name builder tested (28 ms, against 42–55 ms for f-strings or numpy). So the only big lever is not setting names, as JuMP does withset_string_names_on_creation.Changes
Model.set_names_in_solver_ionow defaults toFalse(behaviour change).Solver._buildresolvesset_names=Nonefrom the model, andto_highspy,to_gurobipy,to_mosekandto_xpressnow takeset_names: bool | None = Noneand follow the model setting.Model.solvekeeps names on whenwarmstart_fnorbasis_fnis given, because basis files refer to names. Without this, warm-starting a direct Gurobi solve from a basis written by an LP-file solve raisedGurobiError: variable name x0 doesn't exist.Variable.get_solver_attribute(Gurobi) now maps values by position for the direct API. It used to parse labels fromVarName, which returned the values of the wrong variables when names were off and a variable was masked. It also works on sliced variables.Model._compute_infeasibilities_gurobinow reads theIISConstrattribute instead of parsing constraint names from a written.ilpfile. With Gurobi's default names the old parser returned nothing, and the temporary file was never deleted.Tests
to_highspyfollows the model default and an explicit override.get_solver_attributewith a masked and a sliced variable, forlp, direct with names, and direct without names.lpand direct without names.Benchmark (HiGHS 1.15.1)
Measured breakdown of the name cost: name strings 29 ms, assigning
col_names_/row_names_12 ms,getLp/passModelround trip 25 ms.Checklist
AGENTS.md).doc.doc/release_notes.rstof the upcoming release is included.