Exploit dual degeneracy to reduce the number of integer infeasibilities; add primal simplex - #1685
Exploit dual degeneracy to reduce the number of integer infeasibilities; add primal simplex#1685chris-maes wants to merge 38 commits into
Conversation
…up after dual simplex Fixed the following bugs that were causing primal simplex to cycle: 1) Swapped input/output arguments in b_solve() 2) Incorrectly setting variable status of leaving variable 3) Primal step length was not limited by bounds of entering variable. Also fixed a bug/typo where the basis was reorderd twice after factorization. Added code to switch to phase I if we loose primal feasibility, and switch back to phase II once feasibility is regained. Tested on NETLIB LPs. Only 2 LPs pilot87 and pilot_ja need primal simplex to remove perturbations at the end of the dual simplex solve. Tested on the 14 MIPLIB root relaxations that need primal simplex to remove perturbations at the end of the dual simplex solve.
…mates for root relaxation. Add initial perturbation parameter
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (3)
cpp/src/dual_simplex/primal.cpp (1)
671-697: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReuse
compute_basic_primal_variableshere.Lines 671-697 rebuild
rhs = b - N*x_N, callbasis_update.b_solve, and scatterxBintox.compute_basic_primal_variablesat lines 371-399 performs exactly the same steps, including the same work-estimate terms. The later call sites at lines 800, 1065, and 1069 already use the helper. Calling the helper here keeps one implementation of the reconstruction and avoids future drift between the two copies.♻️ Proposed refactor
- std::vector<f_t> rhs = lp.rhs; - work_estimate += m; - // rhs = b - sum_{j : x_j = l_j} A(:, j) l(j) - sum_{j : x_j = u_j} A(:, j) * - // u(j) - for (i_t k = 0; k < n - m; ++k) { - const i_t j = nonbasic_list[k]; - const i_t col_start = lp.A.col_start[j]; - const i_t col_end = lp.A.col_start[j + 1]; - const f_t xj = x[j]; - for (i_t p = col_start; p < col_end; ++p) { - rhs[lp.A.i[p]] -= xj * lp.A.x[p]; - } - work_estimate += 3.0*(col_end - col_start); - } - work_estimate += 4 * (n - m); - - - std::vector<f_t> xB(m); - work_estimate += m; - - basis_update.b_solve(rhs, xB); - - for (i_t k = 0; k < m; ++k) { - const i_t j = basic_list[k]; - x[j] = xB[k]; - } - work_estimate += 3 * m; + compute_basic_primal_variables(lp, basis_update, basic_list, nonbasic_list, x, work_estimate);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/dual_simplex/primal.cpp` around lines 671 - 697, Replace the duplicated rhs construction, basis solve, and basic-variable scatter block with a call to compute_basic_primal_variables, passing the existing LP, basis/update, nonbasic and basic lists, x, and work_estimate arguments as required. Remove the redundant local rhs/xB logic while preserving the helper’s work-estimate accounting and resulting x values.cpp/src/dual_simplex/solve.hpp (1)
101-106: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe primal entry point drops work-limit support.
solve_linear_program_advancedandsolve_linear_program_with_advanced_basisaccept awork_limit_context_t*, soCUOPT_WORK_LIMITapplies to them. This declaration has no such parameter.primal_phase2andprimal_phase2_with_advanced_basisaccumulatework_estimateand then discard it, so a solve started with--method=4ignores the configured work limit.Add the
work_limit_context_t* work_unit_context = nullptrparameter and record the accumulatedwork_estimatethrough it, or state in a comment that the primal method does not honour the work limit while it is experimental.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/dual_simplex/solve.hpp` around lines 101 - 106, Extend solve_linear_program_with_primal with a work_limit_context_t* work_unit_context = nullptr parameter, then propagate it through primal_phase2 and primal_phase2_with_advanced_basis so their accumulated work_estimate is recorded and CUOPT_WORK_LIMIT is enforced. If work-limit support cannot be implemented, document at the primal entry point that the experimental method intentionally does not honor the limit.cpp/src/dual_simplex/phase2.cpp (1)
2377-2423: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winDocument the stale caller state after
prepare_optimality.
basis_update_mpf_tandcsc_matrix_tuse value-owning members, so the factorization snapshot is safe.prepare_optimalitymutates the basis lists and statuses without refreshing the caller’s cached basis-indexed state. Document at all three call sites that the immediatebreakis required unlessbasic_mark,nonbasic_mark,nonbasic_end,Arow,delta_y_steepest_edge,squared_infeasibilities, andinfeasibility_indicesare rebuilt.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/dual_simplex/phase2.cpp` around lines 2377 - 2423, Document at each of the three call sites where prepare_optimality mutates basis lists and statuses that the caller’s cached basis-indexed state is stale; the immediate break is required unless basic_mark, nonbasic_mark, nonbasic_end, Arow, delta_y_steepest_edge, squared_infeasibilities, and infeasibility_indices are rebuilt. Add this explanation near the affected control flow without changing the existing snapshot or cleanup behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cpp/include/cuopt/mathematical_optimization/constants.h`:
- Around line 195-196: Update the Cython SolverMethod enum to match the C++
method_t values: add Primal with value 4 and assign Unset value 5, preserving
all existing method mappings and ensuring both values convert correctly without
ValueError.
In `@cpp/src/branch_and_bound/branch_and_bound.cpp`:
- Around line 3249-3266: In branch_and_bound_t::check_for_dual_degeneracy and
the related reduced-LP construction, vstatus mapping, and candidate scan paths,
replace every duplicated std::abs(... ) <= 1e-10 check with one shared
zero-reduced-cost predicate. Define a static constexpr f_t zero_reduced_cost_tol
and a small helper in the appropriate branch_and_bound_t scope, then use it
consistently through the restore loop as well.
- Around line 3559-3569: Update pivot_out_integer_variables to avoid
settings_.log.printf on the worker-thread node path; use settings_.log.debug or
a caller-provided logger while preserving solve_node_lp’s logging behavior. Move
solution, basis, status, and basis_update copies until after candidate
construction and filtering, creating them only when at least one pivot candidate
remains.
- Around line 3611-3670: Remove the unused fast-candidate debug computation in
the candidate loop: eliminate the delta_x construction, slack validation via ok,
dense conversion, residual allocation, matrix-vector multiply, and associated
log. Preserve the candidate discovery and bound checks, or guard the entire
diagnostic path behind the existing compile-time debug mechanism such as
CHECK_SLACKS.
- Around line 3422-3470: Update the feasibility-pump loop around
primal_phase2_with_advanced_basis to use the remaining time budget when
assigning primal_settings.time_limit, rather than settings_.time_limit. After
the solve, immediately terminate the pump for TIME_LIMIT, CONCURRENT_LIMIT,
ITERATION_LIMIT, or NUMERICAL statuses; retain the existing OPTIMAL processing
and avoid additional solve attempts for these terminal outcomes.
- Around line 1646-1659: In the solve_node_lp flow shown, guard the node-level
pivot pass by returning or skipping pivot_out_integer_variables when
fractional_variables reports num_fractional == 0. Also add and honor an
appropriate setting to disable this node-level pass entirely, while preserving
the existing behavior when enabled and fractional variables exist.
- Around line 3482-3517: Update the solution nonbasic-variable loop after
get_basis_from_vstatus to iterate over nonbasic_list.size() rather than
lp.num_cols - lp.num_rows, and explicitly handle a non-empty superbasic_list
instead of relying only on the assert. Preserve the existing bound assignment
logic for each valid nonbasic variable and return or otherwise safely stop
before processing an inconsistent basis.
In `@cpp/src/dual_simplex/primal.cpp`:
- Line 1134: Update the iteration-limit check in
primal_phase2_with_advanced_basis to use a greater-than-or-equal comparison, so
calls entering with iter already above iter_limit return ITERATION_LIMIT before
falling through to NUMERICAL.
- Around line 776-781: At the top of the iteration loop in the dual-simplex
solve flow, add a guard that checks the elapsed time from start_time against
settings.time_limit and checks settings.concurrent_halt before calling
phase2_pricing. Return the corresponding TIME_LIMIT or CONCURRENT_LIMIT status
immediately when either condition is met, so the checks also cover continue
paths that do not increment iter.
In `@cpp/src/dual_simplex/solve.cpp`:
- Around line 64-76: Add an INFEASIBLE value to primal_status_t, update
primal_phase2_with_advanced_basis to return it when phase I converges with
positive residual infeasibility, and update map_primal_status_to_lp_status to
map it to lp_status_t::INFEASIBLE. Ensure PRIMAL_UNBOUNDED is emitted only from
phase 2, not when primal_ratio_test finds no blocking variable during phase I;
preserve the existing phase-2 unbounded behavior.
- Around line 769-832: Avoid publishing constructed zero-valued solution fields
for non-optimal results in the solve flow around primal_phase2 and the
subsequent uncrush/copy block. For every primal status other than OPTIMAL (while
preserving the existing CONCURRENT_LIMIT handling), return the mapped status
before uncrushing or copying objective and solution values, or otherwise mark
objectives unavailable consistently with the dual path. Keep the existing
optimal solution computation and propagation unchanged.
- Around line 79-108: Update initialize_slack_basis_vstatus to return whether a
complete basis was built, track covered rows, and select at most one singleton
column per row without requiring its scaled coefficient to equal ±1. At the
primal_phase2 call site, check the returned status and log the existing failure
message before returning NUMERICAL_ISSUES when fewer than m rows are covered,
rather than relying on the assert.
- Around line 343-346: Update the concurrent-halt assignment in the dual-simplex
solve path to require both settings.inside_mip and a terminal solve status
before setting *settings.concurrent_halt to 1. Exclude NUMERICAL, TIME_LIMIT,
ITERATION_LIMIT, and CUTOFF outcomes, while preserving the existing null-pointer
guard and logging behavior.
---
Nitpick comments:
In `@cpp/src/dual_simplex/phase2.cpp`:
- Around line 2377-2423: Document at each of the three call sites where
prepare_optimality mutates basis lists and statuses that the caller’s cached
basis-indexed state is stale; the immediate break is required unless basic_mark,
nonbasic_mark, nonbasic_end, Arow, delta_y_steepest_edge,
squared_infeasibilities, and infeasibility_indices are rebuilt. Add this
explanation near the affected control flow without changing the existing
snapshot or cleanup behavior.
In `@cpp/src/dual_simplex/primal.cpp`:
- Around line 671-697: Replace the duplicated rhs construction, basis solve, and
basic-variable scatter block with a call to compute_basic_primal_variables,
passing the existing LP, basis/update, nonbasic and basic lists, x, and
work_estimate arguments as required. Remove the redundant local rhs/xB logic
while preserving the helper’s work-estimate accounting and resulting x values.
In `@cpp/src/dual_simplex/solve.hpp`:
- Around line 101-106: Extend solve_linear_program_with_primal with a
work_limit_context_t* work_unit_context = nullptr parameter, then propagate it
through primal_phase2 and primal_phase2_with_advanced_basis so their accumulated
work_estimate is recorded and CUOPT_WORK_LIMIT is enforced. If work-limit
support cannot be implemented, document at the primal entry point that the
experimental method intentionally does not honor the limit.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: ccb63a78-ab46-41c7-9f90-b698078576d6
📒 Files selected for processing (11)
cpp/include/cuopt/mathematical_optimization/constants.hcpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hppcpp/src/branch_and_bound/branch_and_bound.cppcpp/src/branch_and_bound/branch_and_bound.hppcpp/src/dual_simplex/phase2.cppcpp/src/dual_simplex/primal.cppcpp/src/dual_simplex/primal.hppcpp/src/dual_simplex/solve.cppcpp/src/dual_simplex/solve.hppcpp/src/math_optimization/solver_settings.cucpp/src/pdlp/solve.cu
| void initialize_slack_basis_vstatus(const lp_problem_t<i_t, f_t>& lp, | ||
| std::vector<variable_status_t>& vstatus) | ||
| { | ||
| const i_t m = lp.num_rows; | ||
| const i_t n = lp.num_cols; | ||
| vstatus.resize(n); | ||
| for (i_t j = 0; j < n; ++j) { | ||
| if (lp.lower[j] == -inf && lp.upper[j] == inf) { | ||
| vstatus[j] = variable_status_t::NONBASIC_FREE; | ||
| } else if (std::abs(lp.upper[j] - lp.lower[j]) < 1e-12) { | ||
| vstatus[j] = variable_status_t::NONBASIC_FIXED; | ||
| } else if (lp.lower[j] > -inf) { | ||
| vstatus[j] = variable_status_t::NONBASIC_LOWER; | ||
| } else { | ||
| vstatus[j] = variable_status_t::NONBASIC_UPPER; | ||
| } | ||
| } | ||
| i_t num_basic = 0; | ||
| for (i_t j = n - 1; j >= 0; --j) { | ||
| const i_t col_start = lp.A.col_start[j]; | ||
| const i_t col_end = lp.A.col_start[j + 1]; | ||
| const i_t nz = col_end - col_start; | ||
| if (nz == 1 && std::abs(lp.A.x[col_start]) == 1.0) { | ||
| vstatus[j] = variable_status_t::BASIC; | ||
| num_basic++; | ||
| } | ||
| if (num_basic == m) { break; } | ||
| } | ||
| assert(num_basic == m); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift
initialize_slack_basis_vstatus can build an invalid or incomplete basis.
Two defects:
-
The unit-coefficient test runs on the scaled problem. The caller applies
scaling(presolved_lp, settings, lp, column_scales, row_scales)at line 762 and then passeslphere. Row and column scaling changes a slack coefficient of ±1 into an arbitrary value, sostd::abs(lp.A.x[col_start]) == 1.0rejects columns that are slacks in the unscaled problem.num_basiccan stay far belowm. -
The loop never checks which row each singleton column covers. Two singleton columns can share a row. The selected set of
mcolumns is then singular, and one row has no basic column.
If num_basic < m, the assert at line 107 fires in debug builds. In release builds get_basis_from_vstatus inside primal_phase2 fills fewer than m entries of basic_list, and the nonbasic_list.size() == n - m assertion no longer holds. factorize_basis then reads uninitialized indices and indexes lp.A out of range.
Track row coverage explicitly, and select at most one singleton column per row. Compare the existing loop at lines 235-245: it is safe only because create_phase1_problem guarantees a full artificial identity.
🐛 Proposed fix: cover each row exactly once and drop the unit-coefficient test
template <typename i_t, typename f_t>
-void initialize_slack_basis_vstatus(const lp_problem_t<i_t, f_t>& lp,
+bool initialize_slack_basis_vstatus(const lp_problem_t<i_t, f_t>& lp,
std::vector<variable_status_t>& vstatus)
{
const i_t m = lp.num_rows;
const i_t n = lp.num_cols;
vstatus.resize(n);
for (i_t j = 0; j < n; ++j) {
if (lp.lower[j] == -inf && lp.upper[j] == inf) {
vstatus[j] = variable_status_t::NONBASIC_FREE;
} else if (std::abs(lp.upper[j] - lp.lower[j]) < 1e-12) {
vstatus[j] = variable_status_t::NONBASIC_FIXED;
} else if (lp.lower[j] > -inf) {
vstatus[j] = variable_status_t::NONBASIC_LOWER;
} else {
vstatus[j] = variable_status_t::NONBASIC_UPPER;
}
}
- i_t num_basic = 0;
- for (i_t j = n - 1; j >= 0; --j) {
- const i_t col_start = lp.A.col_start[j];
- const i_t col_end = lp.A.col_start[j + 1];
- const i_t nz = col_end - col_start;
- if (nz == 1 && std::abs(lp.A.x[col_start]) == 1.0) {
- vstatus[j] = variable_status_t::BASIC;
- num_basic++;
- }
- if (num_basic == m) { break; }
- }
- assert(num_basic == m);
+ // One basic column per row. A singleton column with a nonzero coefficient
+ // spans exactly its own row, so it is a valid basis column after scaling.
+ std::vector<i_t> row_covered(m, 0);
+ i_t num_basic = 0;
+ for (i_t j = n - 1; j >= 0 && num_basic < m; --j) {
+ const i_t col_start = lp.A.col_start[j];
+ const i_t col_end = lp.A.col_start[j + 1];
+ if (col_end - col_start != 1) { continue; }
+ if (lp.A.x[col_start] == 0.0) { continue; }
+ const i_t i = lp.A.i[col_start];
+ if (row_covered[i]) { continue; }
+ row_covered[i] = 1;
+ vstatus[j] = variable_status_t::BASIC;
+ num_basic++;
+ }
+ return num_basic == m;
}Then handle the failure at the call site instead of relying on assert:
std::vector<variable_status_t> vstatus;
if (!initialize_slack_basis_vstatus(lp, vstatus)) {
settings.log.printf("Primal simplex requires a full slack basis.\n");
return lp_status_t::NUMERICAL_ISSUES;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| void initialize_slack_basis_vstatus(const lp_problem_t<i_t, f_t>& lp, | |
| std::vector<variable_status_t>& vstatus) | |
| { | |
| const i_t m = lp.num_rows; | |
| const i_t n = lp.num_cols; | |
| vstatus.resize(n); | |
| for (i_t j = 0; j < n; ++j) { | |
| if (lp.lower[j] == -inf && lp.upper[j] == inf) { | |
| vstatus[j] = variable_status_t::NONBASIC_FREE; | |
| } else if (std::abs(lp.upper[j] - lp.lower[j]) < 1e-12) { | |
| vstatus[j] = variable_status_t::NONBASIC_FIXED; | |
| } else if (lp.lower[j] > -inf) { | |
| vstatus[j] = variable_status_t::NONBASIC_LOWER; | |
| } else { | |
| vstatus[j] = variable_status_t::NONBASIC_UPPER; | |
| } | |
| } | |
| i_t num_basic = 0; | |
| for (i_t j = n - 1; j >= 0; --j) { | |
| const i_t col_start = lp.A.col_start[j]; | |
| const i_t col_end = lp.A.col_start[j + 1]; | |
| const i_t nz = col_end - col_start; | |
| if (nz == 1 && std::abs(lp.A.x[col_start]) == 1.0) { | |
| vstatus[j] = variable_status_t::BASIC; | |
| num_basic++; | |
| } | |
| if (num_basic == m) { break; } | |
| } | |
| assert(num_basic == m); | |
| } | |
| bool initialize_slack_basis_vstatus(const lp_problem_t<i_t, f_t>& lp, | |
| std::vector<variable_status_t>& vstatus) | |
| { | |
| const i_t m = lp.num_rows; | |
| const i_t n = lp.num_cols; | |
| vstatus.resize(n); | |
| for (i_t j = 0; j < n; ++j) { | |
| if (lp.lower[j] == -inf && lp.upper[j] == inf) { | |
| vstatus[j] = variable_status_t::NONBASIC_FREE; | |
| } else if (std::abs(lp.upper[j] - lp.lower[j]) < 1e-12) { | |
| vstatus[j] = variable_status_t::NONBASIC_FIXED; | |
| } else if (lp.lower[j] > -inf) { | |
| vstatus[j] = variable_status_t::NONBASIC_LOWER; | |
| } else { | |
| vstatus[j] = variable_status_t::NONBASIC_UPPER; | |
| } | |
| } | |
| // One basic column per row. A singleton column with a nonzero coefficient | |
| // spans exactly its own row, so it is a valid basis column after scaling. | |
| std::vector<i_t> row_covered(m, 0); | |
| i_t num_basic = 0; | |
| for (i_t j = n - 1; j >= 0 && num_basic < m; --j) { | |
| const i_t col_start = lp.A.col_start[j]; | |
| const i_t col_end = lp.A.col_start[j + 1]; | |
| if (col_end - col_start != 1) { continue; } | |
| if (lp.A.x[col_start] == 0.0) { continue; } | |
| const i_t i = lp.A.i[col_start]; | |
| if (row_covered[i]) { continue; } | |
| row_covered[i] = 1; | |
| vstatus[j] = variable_status_t::BASIC; | |
| num_basic++; | |
| } | |
| return num_basic == m; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cpp/src/dual_simplex/solve.cpp` around lines 79 - 108, Update
initialize_slack_basis_vstatus to return whether a complete basis was built,
track covered rows, and select at most one singleton column per row without
requiring its scaled coefficient to equal ±1. At the primal_phase2 call site,
check the returned status and log the existing failure message before returning
NUMERICAL_ISSUES when fewer than m rows are covered, rather than relying on the
assert.
| if (settings.inside_mip && settings.concurrent_halt != nullptr) { | ||
| settings.log.printf("Setting concurrent halt to 1 inside_mip\n"); | ||
| *settings.concurrent_halt = 1; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Gate the concurrent-halt flag on a terminal status.
This block sets *settings.concurrent_halt = 1 for every status value, including NUMERICAL, TIME_LIMIT, ITERATION_LIMIT, and CUTOFF. The previous phase-2 logic that set the flag was removed from phase2.cpp. Cooperating solvers now stop even when this dual solve produced no usable answer, so a concurrent MIP root solve can lose a PDLP result that would have finished.
Set the flag only after the solve reaches a terminal outcome.
🐛 Proposed fix
- if (settings.inside_mip && settings.concurrent_halt != nullptr) {
+ if (settings.inside_mip && settings.concurrent_halt != nullptr &&
+ (status == dual_status_t::OPTIMAL || status == dual_status_t::DUAL_UNBOUNDED ||
+ status == dual_status_t::CUTOFF)) {
settings.log.printf("Setting concurrent halt to 1 inside_mip\n");
*settings.concurrent_halt = 1;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (settings.inside_mip && settings.concurrent_halt != nullptr) { | |
| settings.log.printf("Setting concurrent halt to 1 inside_mip\n"); | |
| *settings.concurrent_halt = 1; | |
| } | |
| if (settings.inside_mip && settings.concurrent_halt != nullptr && | |
| (status == dual_status_t::OPTIMAL || status == dual_status_t::DUAL_UNBOUNDED || | |
| status == dual_status_t::CUTOFF)) { | |
| settings.log.printf("Setting concurrent halt to 1 inside_mip\n"); | |
| *settings.concurrent_halt = 1; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cpp/src/dual_simplex/solve.cpp` around lines 343 - 346, Update the
concurrent-halt assignment in the dual-simplex solve path to require both
settings.inside_mip and a terminal solve status before setting
*settings.concurrent_halt to 1. Exclude NUMERICAL, TIME_LIMIT, ITERATION_LIMIT,
and CUTOFF outcomes, while preserving the existing null-pointer guard and
logging behavior.
| const primal_status_t primal_status = | ||
| primal_phase2(2, start_time, lp, settings, vstatus, lp_solution, iter); | ||
| lp_solution.iterations = iter; | ||
| original_solution.iterations = iter; | ||
|
|
||
| if (primal_status == primal_status_t::CONCURRENT_LIMIT) { | ||
| solution.iterations = iter; | ||
| return lp_status_t::CONCURRENT_LIMIT; | ||
| } | ||
|
|
||
| if (primal_status == primal_status_t::OPTIMAL) { | ||
| lp_solution.objective = compute_objective(lp, lp_solution.x); | ||
| lp_solution.user_objective = compute_user_objective(lp, lp_solution.objective); | ||
|
|
||
| std::vector<f_t> residual = lp.rhs; | ||
| matrix_vector_multiply(lp.A, 1.0, lp_solution.x, -1.0, residual); | ||
| lp_solution.l2_primal_residual = vector_norm2<i_t, f_t>(residual); | ||
|
|
||
| std::vector<f_t> dual_residual = lp_solution.z; | ||
| for (i_t j = 0; j < lp.num_cols; ++j) { | ||
| dual_residual[j] -= lp.objective[j]; | ||
| } | ||
| matrix_transpose_vector_multiply(lp.A, 1.0, lp_solution.y, 1.0, dual_residual); | ||
| lp_solution.l2_dual_residual = vector_norm2<i_t, f_t>(dual_residual); | ||
|
|
||
| std::vector<f_t> unscaled_x(lp.num_cols); | ||
| std::vector<f_t> unscaled_y(lp.num_rows); | ||
| std::vector<f_t> unscaled_z(lp.num_cols); | ||
| unscale_solution<i_t, f_t>(column_scales, | ||
| row_scales, | ||
| lp_solution.x, | ||
| lp_solution.y, | ||
| lp_solution.z, | ||
| unscaled_x, | ||
| unscaled_y, | ||
| unscaled_z); | ||
| uncrush_solution(presolve_info, | ||
| settings, | ||
| original_lp, | ||
| unscaled_x, | ||
| unscaled_y, | ||
| unscaled_z, | ||
| original_solution.x, | ||
| original_solution.y, | ||
| original_solution.z); | ||
| original_solution.objective = lp_solution.objective; | ||
| original_solution.user_objective = lp_solution.user_objective; | ||
| original_solution.l2_primal_residual = lp_solution.l2_primal_residual; | ||
| original_solution.l2_dual_residual = lp_solution.l2_dual_residual; | ||
| } | ||
|
|
||
| uncrush_primal_solution(user_problem, original_lp, original_solution.x, solution.x); | ||
| uncrush_dual_solution(user_problem, | ||
| original_lp, | ||
| original_solution.y, | ||
| original_solution.z, | ||
| solution.y, | ||
| solution.z); | ||
| solution.objective = original_solution.objective; | ||
| solution.user_objective = original_solution.user_objective; | ||
| solution.iterations = original_solution.iterations; | ||
| solution.l2_primal_residual = original_solution.l2_primal_residual; | ||
| solution.l2_dual_residual = original_solution.l2_dual_residual; | ||
| return map_primal_status_to_lp_status(primal_status); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Do not publish a zeroed solution for a non-optimal primal status.
The block at lines 779-818 runs only for primal_status_t::OPTIMAL. For TIME_LIMIT, ITERATION_LIMIT, and NUMERICAL, original_solution.x, y, and z keep their constructed values, and objective and user_objective are never assigned. Lines 820-831 then uncrush that zero vector into solution and copy those objective fields. The caller run_primal in cpp/src/pdlp/solve.cu converts the returned solution for every status, so a time-limited primal solve reports a zero primal point with a zero objective as if it were a real iterate.
The dual path handles this differently: solve_linear_program_with_advanced_basis fills original_solution only on OPTIMAL, and it sets user_objective explicitly for the unbounded case.
Return early for the non-optimal statuses, or set the objective fields to a sentinel that marks them as unavailable.
🐛 Proposed fix
if (primal_status == primal_status_t::CONCURRENT_LIMIT) {
solution.iterations = iter;
return lp_status_t::CONCURRENT_LIMIT;
}
- if (primal_status == primal_status_t::OPTIMAL) {
+ if (primal_status != primal_status_t::OPTIMAL) {
+ // No verified iterate to report. Leave the solution vectors untouched.
+ solution.iterations = iter;
+ return map_primal_status_to_lp_status(primal_status);
+ }
+
+ {
lp_solution.objective = compute_objective(lp, lp_solution.x);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const primal_status_t primal_status = | |
| primal_phase2(2, start_time, lp, settings, vstatus, lp_solution, iter); | |
| lp_solution.iterations = iter; | |
| original_solution.iterations = iter; | |
| if (primal_status == primal_status_t::CONCURRENT_LIMIT) { | |
| solution.iterations = iter; | |
| return lp_status_t::CONCURRENT_LIMIT; | |
| } | |
| if (primal_status == primal_status_t::OPTIMAL) { | |
| lp_solution.objective = compute_objective(lp, lp_solution.x); | |
| lp_solution.user_objective = compute_user_objective(lp, lp_solution.objective); | |
| std::vector<f_t> residual = lp.rhs; | |
| matrix_vector_multiply(lp.A, 1.0, lp_solution.x, -1.0, residual); | |
| lp_solution.l2_primal_residual = vector_norm2<i_t, f_t>(residual); | |
| std::vector<f_t> dual_residual = lp_solution.z; | |
| for (i_t j = 0; j < lp.num_cols; ++j) { | |
| dual_residual[j] -= lp.objective[j]; | |
| } | |
| matrix_transpose_vector_multiply(lp.A, 1.0, lp_solution.y, 1.0, dual_residual); | |
| lp_solution.l2_dual_residual = vector_norm2<i_t, f_t>(dual_residual); | |
| std::vector<f_t> unscaled_x(lp.num_cols); | |
| std::vector<f_t> unscaled_y(lp.num_rows); | |
| std::vector<f_t> unscaled_z(lp.num_cols); | |
| unscale_solution<i_t, f_t>(column_scales, | |
| row_scales, | |
| lp_solution.x, | |
| lp_solution.y, | |
| lp_solution.z, | |
| unscaled_x, | |
| unscaled_y, | |
| unscaled_z); | |
| uncrush_solution(presolve_info, | |
| settings, | |
| original_lp, | |
| unscaled_x, | |
| unscaled_y, | |
| unscaled_z, | |
| original_solution.x, | |
| original_solution.y, | |
| original_solution.z); | |
| original_solution.objective = lp_solution.objective; | |
| original_solution.user_objective = lp_solution.user_objective; | |
| original_solution.l2_primal_residual = lp_solution.l2_primal_residual; | |
| original_solution.l2_dual_residual = lp_solution.l2_dual_residual; | |
| } | |
| uncrush_primal_solution(user_problem, original_lp, original_solution.x, solution.x); | |
| uncrush_dual_solution(user_problem, | |
| original_lp, | |
| original_solution.y, | |
| original_solution.z, | |
| solution.y, | |
| solution.z); | |
| solution.objective = original_solution.objective; | |
| solution.user_objective = original_solution.user_objective; | |
| solution.iterations = original_solution.iterations; | |
| solution.l2_primal_residual = original_solution.l2_primal_residual; | |
| solution.l2_dual_residual = original_solution.l2_dual_residual; | |
| return map_primal_status_to_lp_status(primal_status); | |
| const primal_status_t primal_status = | |
| primal_phase2(2, start_time, lp, settings, vstatus, lp_solution, iter); | |
| lp_solution.iterations = iter; | |
| original_solution.iterations = iter; | |
| if (primal_status == primal_status_t::CONCURRENT_LIMIT) { | |
| solution.iterations = iter; | |
| return lp_status_t::CONCURRENT_LIMIT; | |
| } | |
| if (primal_status != primal_status_t::OPTIMAL) { | |
| // No verified iterate to report. Leave the solution vectors untouched. | |
| solution.iterations = iter; | |
| return map_primal_status_to_lp_status(primal_status); | |
| } | |
| { | |
| lp_solution.objective = compute_objective(lp, lp_solution.x); | |
| lp_solution.user_objective = compute_user_objective(lp, lp_solution.objective); | |
| std::vector<f_t> residual = lp.rhs; | |
| matrix_vector_multiply(lp.A, 1.0, lp_solution.x, -1.0, residual); | |
| lp_solution.l2_primal_residual = vector_norm2<i_t, f_t>(residual); | |
| std::vector<f_t> dual_residual = lp_solution.z; | |
| for (i_t j = 0; j < lp.num_cols; ++j) { | |
| dual_residual[j] -= lp.objective[j]; | |
| } | |
| matrix_transpose_vector_multiply(lp.A, 1.0, lp_solution.y, 1.0, dual_residual); | |
| lp_solution.l2_dual_residual = vector_norm2<i_t, f_t>(dual_residual); | |
| std::vector<f_t> unscaled_x(lp.num_cols); | |
| std::vector<f_t> unscaled_y(lp.num_rows); | |
| std::vector<f_t> unscaled_z(lp.num_cols); | |
| unscale_solution<i_t, f_t>(column_scales, | |
| row_scales, | |
| lp_solution.x, | |
| lp_solution.y, | |
| lp_solution.z, | |
| unscaled_x, | |
| unscaled_y, | |
| unscaled_z); | |
| uncrush_solution(presolve_info, | |
| settings, | |
| original_lp, | |
| unscaled_x, | |
| unscaled_y, | |
| unscaled_z, | |
| original_solution.x, | |
| original_solution.y, | |
| original_solution.z); | |
| original_solution.objective = lp_solution.objective; | |
| original_solution.user_objective = lp_solution.user_objective; | |
| original_solution.l2_primal_residual = lp_solution.l2_primal_residual; | |
| original_solution.l2_dual_residual = lp_solution.l2_dual_residual; | |
| } | |
| uncrush_primal_solution(user_problem, original_lp, original_solution.x, solution.x); | |
| uncrush_dual_solution(user_problem, | |
| original_lp, | |
| original_solution.y, | |
| original_solution.z, | |
| solution.y, | |
| solution.z); | |
| solution.objective = original_solution.objective; | |
| solution.user_objective = original_solution.user_objective; | |
| solution.iterations = original_solution.iterations; | |
| solution.l2_primal_residual = original_solution.l2_primal_residual; | |
| solution.l2_dual_residual = original_solution.l2_dual_residual; | |
| return map_primal_status_to_lp_status(primal_status); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cpp/src/dual_simplex/solve.cpp` around lines 769 - 832, Avoid publishing
constructed zero-valued solution fields for non-optimal results in the solve
flow around primal_phase2 and the subsequent uncrush/copy block. For every
primal status other than OPTIMAL (while preserving the existing CONCURRENT_LIMIT
handling), return the mapped status before uncrushing or copying objective and
solution values, or otherwise mark objectives unavailable consistently with the
dual path. Keep the existing optimal solution computation and propagation
unchanged.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughChangesThe PR adds an experimental primal simplex method, public solver settings and APIs, work-estimate propagation, PDLP dispatch, and branch-and-bound integer-pivot and feasibility-pump processing. Primal simplex support
Branch-and-bound integration
Estimated code review effort: 5 (Critical) | ~120 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (7)
cpp/src/branch_and_bound/branch_and_bound.hpp (1)
346-349: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDocument the append contract of
check_for_dual_degeneracy.The definition appends to
zero_reduced_costs_varsandzero_reduced_costs_vars_nonbasic_indexwithout clearing them first. Both current callers pass freshly declared vectors, so the behavior is correct today. State the contract here, or clear the vectors in the definition, so a future caller that reuses a buffer does not silently accumulate stale indices.The neighboring
apply_delta_x_for_integer_pivotdeclaration already documents its mutation contract; match that level of detail for the other three new methods.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/branch_and_bound/branch_and_bound.hpp` around lines 346 - 349, Document in the declaration of check_for_dual_degeneracy that zero_reduced_costs_vars and zero_reduced_costs_vars_nonbasic_index are output buffers whose values are appended without being cleared, requiring callers to provide empty or intentionally reusable buffers; match the mutation-contract detail used by apply_delta_x_for_integer_pivot.cpp/src/branch_and_bound/branch_and_bound.cpp (1)
3387-3393: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the unused
reduced_edge_norms.
primal_phase2_with_advanced_basistakes no edge-norm argument (seecpp/src/dual_simplex/primal.hpp:45-59).reduced_edge_normsis allocated and filled withlp.num_colsreads ofedge_norms_, then discarded. Delete it, or pass it once the primal solver accepts steepest-edge norms.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/branch_and_bound/branch_and_bound.cpp` around lines 3387 - 3393, Remove the unused reduced_edge_norms allocation and population loop in the surrounding branch-and-bound code, including the reduced_col assignment. Leave edge_norms_ untouched since primal_phase2_with_advanced_basis does not accept or use edge-norm data.cpp/src/dual_simplex/primal.cpp (4)
663-665: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
incoming_xandincoming_vstatusare never read. Both copies costO(n)time and two allocations per call, and the accounted work at line 665 charges2nfor them. No later code uses either value. The caller incpp/src/dual_simplex/phase2.cpp(lines 2384-2390) takes its own snapshot for rollback, so the intended purpose appears unimplemented here.Either remove both copies, or use them to restore state on the non-
OPTIMALreturn paths at lines 984, 905, 1058, and 1136. Do you want me to open an issue to track the rollback behavior?🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/dual_simplex/primal.cpp` around lines 663 - 665, Remove the unused incoming_x and incoming_vstatus copies from the relevant primal simplex routine, and remove the associated work_estimate += 2.0 * n accounting. Do not add rollback behavior here, since the caller already snapshots state and no code in this routine consumes these values.
777-780: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winInitialize
direction.phase2_pricingwritesdirectiononly when it selects a candidate. Lines 947 and 955 read it. Every current path that reaches line 936 hasentering_index != -1, so the value is defined today, but the invariant now spans three separate retry paths (lines 839, 886) that reuse the same variable. Initialize it to0so a future path cannot read an indeterminate value.- i_t direction; + i_t direction = 0;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/dual_simplex/primal.cpp` around lines 777 - 780, Initialize the direction variable to 0 at its declaration before the phase2_pricing call in the primal simplex flow. Keep the existing phase2_pricing and retry-path behavior unchanged while ensuring later reads of direction remain defined if no candidate is selected.
715-728: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe
phaseparameter is ignored. Lines 724 and 727 overwritephaseunconditionally from the measured primal infeasibility, so the caller's argument has no effect.cpp/src/dual_simplex/phase2.cppline 2397 passes2, and the declaration incpp/src/dual_simplex/primal.hpppresentsphaseas an input.Remove the parameter, or honor it when the caller already knows the starting phase.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/dual_simplex/primal.cpp` around lines 715 - 728, Update the phase-selection logic in the primal routine to honor the incoming phase value instead of unconditionally overwriting it based on primal infeasibility. Preserve the existing phase 1 setup and logging when phase 1 is selected, and retain phase 2 behavior for callers passing phase 2; update the declaration and call sites consistently if removing the parameter instead.
431-431: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
settings.pivot_tolinstead of a hardcoded constant.simplex_solver_settings_texposespivot_tol(default1e-7), and the dual simplex ratio test reads it. This function hardcodes1e-8, so tuning the setting has no effect on the primal ratio test and the two ratio tests disagree on what counts as a usable pivot.- constexpr f_t pivot_tol = 1e-8; + const f_t pivot_tol = settings.pivot_tol;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/dual_simplex/primal.cpp` at line 431, In the primal ratio-test function containing the local pivot_tol declaration, replace the hardcoded 1e-8 value with settings.pivot_tol from simplex_solver_settings_t. Preserve the existing ratio-test logic while ensuring configured pivot tolerance controls primal pivot usability consistently with the dual simplex path.cpp/src/dual_simplex/phase2.cpp (1)
2335-2348: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the redundant
x,y,zparameters, or document that they must aliassol.prepare_optimalitynow mutatessolthrough the primal cleanup at lines 2397-2408, butxis still declaredconst std::vector<f_t>&. The function reads the cleaned values at line 2434 only because the caller bindsx,y, andztosol.x,sol.y, andsol.z(lines 2624-2626). Theconstqualifier hides that requirement. If any future caller passes a copy, the reported primal infeasibility silently describes the pre-cleanup point.Pass
solalone and usesol.x,sol.y,sol.zinside the function.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/dual_simplex/phase2.cpp` around lines 2335 - 2348, Update prepare_optimality to remove the redundant x, y, and z parameters, then use sol.x, sol.y, and sol.z for all corresponding reads inside the function. Update every caller, including the call near the existing sol.x/sol.y/sol.z bindings, to pass only sol and preserve the post-cleanup values used for optimality reporting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cpp/src/branch_and_bound/branch_and_bound.cpp`:
- Around line 3504-3522: Before calling basis_update.refactor_basis, copy
vstatus, basic_list, nonbasic_list, and basis_update; on every non-zero
refactor_status, restore all four from those snapshots before returning. Remove
the obsolete TODO while preserving the existing concurrent-halt, time-limit, and
logging behavior.
- Around line 3192-3199: Update dual_degenerate_feasibility_pump and
pivot_out_integer_variables to return their accumulated simplex-iteration counts
(iter and work_estimate), then add both returned values to
exploration_stats_.total_simplex_iters at the surrounding cut-pass call sites,
consistent with dual_phase2_with_advanced_basis. Preserve the existing algorithm
behavior while ensuring Iter/Node reporting and the
branch_and_bound_simplex_iteration_limit include this work.
- Around line 3645-3670: The refactorization failure paths in the pivot routine
must signal failure instead of returning as though the pivot succeeded. Update
the enclosing pivot method and its caller around recommend_refactor,
factorize_basis, and the vstatus_copy success check to return or propagate a
boolean failure result for concurrent halt, time limit, invalid rank, or
incomplete rank; ensure the caller stops the pivot pass and does not commit the
mutated basic_list, nonbasic_list, vstatus, or solution.x.
- Line 4101: Guard the work-rate calculation in the logging statement using
root_relax_elapsed_time so zero elapsed time cannot produce an inf or nan value;
retain the existing work-rate output for positive elapsed times and use a finite
fallback when the duration is zero.
- Around line 3309-3341: Derive the reduced column count by scanning all
lp.num_cols with the same BASIC-or-zero-reduced-cost predicate used when
populating A_reduced, rather than using lp.num_rows plus
zero_reduced_costs_vars.size(). Before constructing lp_reduced, compare this
count with the expected basic plus zero-reduced-cost total and return early on
mismatch, preventing out-of-bounds writes in the reduced-column arrays and
reduced_vstatus.
In `@cpp/src/dual_simplex/phase2.cpp`:
- Around line 2409-2426: Update the primal cleanup result handling in
primal_phase2_with_advanced_basis to distinguish TIME_LIMIT and CONCURRENT_LIMIT
from numerical failure: detect exhausted settings.time_limit or an asserted
*settings.concurrent_halt, return the corresponding limit status, and preserve
the existing state restoration for every non-OPTIMAL outcome. Ensure
dual_phase2_with_advanced_basis receives these statuses and maps them to
dual_status_t::TIME_LIMIT or dual_status_t::CONCURRENT_LIMIT instead of
reporting OPTIMAL.
- Around line 2674-2677: Update the phase-2 condition around
phase2::initial_perturbation so the documented initial_perturbation value -1
follows an automatic policy, such as enabling perturbation in phase 2 alongside
value 1. Preserve value 0 as disabled and keep the existing phase == 2 guard.
- Line 2607: Update the phase-2 horizon reporting flow around
record_work_sync_on_horizon so phase2_work_estimate remains cumulative and
caller-owned. Replace the resets at the reporting sites near lines 2883 and 3757
with a separate reported baseline or delta, preserving initialization work from
root_relax_work_estimate while reporting only newly accumulated work.
In `@cpp/src/dual_simplex/primal.cpp`:
- Around line 494-499: Update the ratio-selection logic in both the lower- and
upper-bound branches around basic_leaving, leaving_index, and current_dx so
near-ties do not assign a larger value to min_val. Keep min_val unchanged when
ratio is within the 1e-9 tie tolerance, while still updating the selected
leaving row and current_dx; only a strictly smaller ratio should replace
min_val.
In `@cpp/src/dual_simplex/simplex_solver_settings.hpp`:
- Line 170: Initialize initial_perturbation to -1 in simplex_solver_settings_t,
either alongside ordering(-1) in the constructor or via a default member
initializer, so default-constructed settings use automatic perturbation.
---
Nitpick comments:
In `@cpp/src/branch_and_bound/branch_and_bound.cpp`:
- Around line 3387-3393: Remove the unused reduced_edge_norms allocation and
population loop in the surrounding branch-and-bound code, including the
reduced_col assignment. Leave edge_norms_ untouched since
primal_phase2_with_advanced_basis does not accept or use edge-norm data.
In `@cpp/src/branch_and_bound/branch_and_bound.hpp`:
- Around line 346-349: Document in the declaration of check_for_dual_degeneracy
that zero_reduced_costs_vars and zero_reduced_costs_vars_nonbasic_index are
output buffers whose values are appended without being cleared, requiring
callers to provide empty or intentionally reusable buffers; match the
mutation-contract detail used by apply_delta_x_for_integer_pivot.
In `@cpp/src/dual_simplex/phase2.cpp`:
- Around line 2335-2348: Update prepare_optimality to remove the redundant x, y,
and z parameters, then use sol.x, sol.y, and sol.z for all corresponding reads
inside the function. Update every caller, including the call near the existing
sol.x/sol.y/sol.z bindings, to pass only sol and preserve the post-cleanup
values used for optimality reporting.
In `@cpp/src/dual_simplex/primal.cpp`:
- Around line 663-665: Remove the unused incoming_x and incoming_vstatus copies
from the relevant primal simplex routine, and remove the associated
work_estimate += 2.0 * n accounting. Do not add rollback behavior here, since
the caller already snapshots state and no code in this routine consumes these
values.
- Around line 777-780: Initialize the direction variable to 0 at its declaration
before the phase2_pricing call in the primal simplex flow. Keep the existing
phase2_pricing and retry-path behavior unchanged while ensuring later reads of
direction remain defined if no candidate is selected.
- Around line 715-728: Update the phase-selection logic in the primal routine to
honor the incoming phase value instead of unconditionally overwriting it based
on primal infeasibility. Preserve the existing phase 1 setup and logging when
phase 1 is selected, and retain phase 2 behavior for callers passing phase 2;
update the declaration and call sites consistently if removing the parameter
instead.
- Line 431: In the primal ratio-test function containing the local pivot_tol
declaration, replace the hardcoded 1e-8 value with settings.pivot_tol from
simplex_solver_settings_t. Preserve the existing ratio-test logic while ensuring
configured pivot tolerance controls primal pivot usability consistently with the
dual simplex path.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 23872084-8518-4198-a1f6-2d3c479d2ba8
📒 Files selected for processing (17)
cpp/include/cuopt/mathematical_optimization/constants.hcpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hppcpp/src/branch_and_bound/branch_and_bound.cppcpp/src/branch_and_bound/branch_and_bound.hppcpp/src/branch_and_bound/pseudo_costs.cppcpp/src/dual_simplex/basis_updates.cppcpp/src/dual_simplex/basis_updates.hppcpp/src/dual_simplex/crossover.cppcpp/src/dual_simplex/phase2.cppcpp/src/dual_simplex/phase2.hppcpp/src/dual_simplex/primal.cppcpp/src/dual_simplex/primal.hppcpp/src/dual_simplex/simplex_solver_settings.hppcpp/src/dual_simplex/solve.cppcpp/src/dual_simplex/solve.hppcpp/src/math_optimization/solver_settings.cucpp/src/pdlp/solve.cu
| dual_degenerate_feasibility_pump(original_lp_, | ||
| basic_list, | ||
| nonbasic_list, | ||
| root_vstatus_, | ||
| root_relax_soln_, | ||
| basis_update, | ||
| num_fractional, | ||
| fractional); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Account for the pump's simplex iterations.
dual_degenerate_feasibility_pump runs up to max_pump_iter primal simplex solves and keeps the count in its local iter. That count is never added to exploration_stats_.total_simplex_iters. The same applies to pivot_out_integer_variables, which accumulates a local work_estimate only.
Two consequences: the reported Iter/Node value understates real work, and settings_.branch_and_bound_simplex_iteration_limit no longer bounds total simplex effort once the pump runs on every cut pass. Return the iteration count from both routines and accumulate it, as the surrounding cut-pass code already does for dual_phase2_with_advanced_basis at line 3140.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cpp/src/branch_and_bound/branch_and_bound.cpp` around lines 3192 - 3199,
Update dual_degenerate_feasibility_pump and pivot_out_integer_variables to
return their accumulated simplex-iteration counts (iter and work_estimate), then
add both returned values to exploration_stats_.total_simplex_iters at the
surrounding cut-pass call sites, consistent with
dual_phase2_with_advanced_basis. Preserve the existing algorithm behavior while
ensuring Iter/Node reporting and the branch_and_bound_simplex_iteration_limit
include this work.
| const i_t refactor_status = basis_update.refactor_basis(lp.A, | ||
| settings_, | ||
| lp.lower, | ||
| lp.upper, | ||
| exploration_stats_.start_time, | ||
| basic_list, | ||
| nonbasic_list, | ||
| vstatus); | ||
| if (refactor_status == CONCURRENT_HALT_RETURN || refactor_status == TIME_LIMIT_RETURN) { | ||
| // TODO: On failure vstatus, basic_list, and nonbasic_list are in a bad state. | ||
| // We should save copies before the failure and restore them after the failure. | ||
| return; | ||
| } | ||
| if (refactor_status != 0) { | ||
| settings_.log.printf("Failed to refactor basis after dual degenerate feasibility pump. " | ||
| "%d deficient columns.\n", | ||
| refactor_status); | ||
| return; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Save and restore the basis state before refactor_basis can fail.
The TODO on line 3513 describes the defect exactly. vstatus, basic_list, and nonbasic_list are already overwritten when refactor_basis returns non-zero. On every failure path this function returns with a basis that no longer matches basis_update, and the caller (root processing at line 4153 or do_cut_pass at line 3192) continues to use it.
Copy the three containers plus basis_update before the translation, and restore them on any non-zero refactor_status. Do you want me to open an issue to track this?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cpp/src/branch_and_bound/branch_and_bound.cpp` around lines 3504 - 3522,
Before calling basis_update.refactor_basis, copy vstatus, basic_list,
nonbasic_list, and basis_update; on every non-zero refactor_status, restore all
four from those snapshots before returning. Remove the obsolete TODO while
preserving the existing concurrent-halt, time-limit, and logging behavior.
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
cpp/src/branch_and_bound/branch_and_bound.cpp (2)
3480-3492: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove the per-iteration pump logs to
debug.
dual_degenerate_feasibility_pumpruns once at the root and once per cut pass. Each run prints one construction line (line 3364), up tomax_pump_iterprogress lines (line 3480), and one summary line (line 3492).pivot_out_integer_variablesadds more at lines 3743, 3862, and 3921.settings_.log.printfwrites at the default verbosity, so a model with many cut passes gains dozens of new lines in normal output. Usesettings_.log.debugfor the per-iteration and per-candidate lines. Keep at most the final summary atprintf.♻️ Proposed logging change
- settings_.log.printf( - "Degenerate feasibility pump (%d/%d): primal work estimate %.2e, iter %d, fractional variables %d/%d. Time %.2f\n", pump_iter, max_pump_iter, primal_work_estimate, iter, num_fractional_reduced, num_fractional, toc(dual_degenerate_feasibility_pump_start_time)); + settings_.log.debug( + "Degenerate feasibility pump (%d/%d): primal work estimate %.2e, iter %d, fractional " + "variables %d/%d. Time %.2f\n", + pump_iter, + max_pump_iter, + primal_work_estimate, + iter, + num_fractional_reduced, + num_fractional, + toc(dual_degenerate_feasibility_pump_start_time));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/branch_and_bound/branch_and_bound.cpp` around lines 3480 - 3492, Change the pump construction, progress, and candidate log calls in dual_degenerate_feasibility_pump and pivot_out_integer_variables from settings_.log.printf to settings_.log.debug, while keeping only the final summary in dual_degenerate_feasibility_pump at printf.
3405-3405: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueVary the pump seed across calls, deterministically.
The pump constructs
rngfromsettings_.random_seedon every call. The root call and every cut-pass call therefore draw the same perturbation sequence. When the same stall repeats, the perturbation repeats too, so the stall-breaking logic at lines 3417-3427 loses effect. Mix a per-call counter into the seed so successive calls differ while the run stays reproducible.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cpp/src/branch_and_bound/branch_and_bound.cpp` at line 3405, Update the RNG initialization in the pump routine to mix a persistent per-call counter with settings_.random_seed, ensuring each successive call gets a distinct but deterministic seed; preserve reproducibility across runs and the existing random-seed behavior otherwise.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@cpp/src/branch_and_bound/branch_and_bound.cpp`:
- Around line 3480-3492: Change the pump construction, progress, and candidate
log calls in dual_degenerate_feasibility_pump and pivot_out_integer_variables
from settings_.log.printf to settings_.log.debug, while keeping only the final
summary in dual_degenerate_feasibility_pump at printf.
- Line 3405: Update the RNG initialization in the pump routine to mix a
persistent per-call counter with settings_.random_seed, ensuring each successive
call gets a distinct but deterministic seed; preserve reproducibility across
runs and the existing random-seed behavior otherwise.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 2ac41597-1477-4f32-a2b5-d677f03bd644
📒 Files selected for processing (17)
cpp/include/cuopt/mathematical_optimization/constants.hcpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hppcpp/src/branch_and_bound/branch_and_bound.cppcpp/src/branch_and_bound/branch_and_bound.hppcpp/src/branch_and_bound/pseudo_costs.cppcpp/src/dual_simplex/basis_updates.cppcpp/src/dual_simplex/basis_updates.hppcpp/src/dual_simplex/crossover.cppcpp/src/dual_simplex/phase2.cppcpp/src/dual_simplex/phase2.hppcpp/src/dual_simplex/primal.cppcpp/src/dual_simplex/primal.hppcpp/src/dual_simplex/simplex_solver_settings.hppcpp/src/dual_simplex/solve.cppcpp/src/dual_simplex/solve.hppcpp/src/math_optimization/solver_settings.cucpp/src/pdlp/solve.cu
🚧 Files skipped from review as they are similar to previous changes (16)
- cpp/src/math_optimization/solver_settings.cu
- cpp/src/dual_simplex/simplex_solver_settings.hpp
- cpp/src/branch_and_bound/pseudo_costs.cpp
- cpp/src/dual_simplex/basis_updates.hpp
- cpp/src/dual_simplex/phase2.hpp
- cpp/src/dual_simplex/primal.hpp
- cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp
- cpp/src/branch_and_bound/branch_and_bound.hpp
- cpp/include/cuopt/mathematical_optimization/constants.h
- cpp/src/dual_simplex/crossover.cpp
- cpp/src/dual_simplex/solve.cpp
- cpp/src/pdlp/solve.cu
- cpp/src/dual_simplex/basis_updates.cpp
- cpp/src/dual_simplex/solve.hpp
- cpp/src/dual_simplex/phase2.cpp
- cpp/src/dual_simplex/primal.cpp
| } | ||
| } | ||
| if (x[j] > lp.upper[j]) { | ||
| // x_j > u_j => x_j - u_j > 0 |
| if (x[j] > lp.upper[j]) { | ||
| // x_j > u_j => x_j - u_j > 0 | ||
| if (x[j] > lp.upper[j] + primal_tol) { | ||
| // x_j > u_j + tol => violation exceeds per-variable threshold |
| * PDLP: Use the PDLP method. | ||
| * DualSimplex: Use the dual simplex method. | ||
| * Barrier: Use the barrier method | ||
| * Primal: Use the (experimental) primal simplex method. |
There was a problem hiding this comment.
Remove experimental
| #define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns" | ||
| #define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic" | ||
| #define CUOPT_PRESOLVE "presolve" | ||
| #define CUOPT_INITIAL_PERTURBATION "initial_perturbation" |
There was a problem hiding this comment.
Make this more explicit (like CUOPT_INITIAL_PRIMAL_PERTURBATION) if its related to primal only.
| simplex_solver_settings_t<i_t, f_t> primal_settings = settings; | ||
| primal_settings.iteration_limit = std::numeric_limits<i_t>::max(); | ||
| primal_status_t primal_status = | ||
| primal_phase2(2, start_time, lp, primal_settings, vstatus, solution, primal_iter); |
There was a problem hiding this comment.
work estimates are not piped in here?
| case primal_status_t::CONCURRENT_LIMIT: return lp_status_t::CONCURRENT_LIMIT; | ||
| case primal_status_t::NUMERICAL: | ||
| case primal_status_t::NOT_LOADED: | ||
| default: return lp_status_t::NUMERICAL_ISSUES; |
There was a problem hiding this comment.
Do not implement the default case. That way, you will get a compilation error when a new status is added.
| } else if (settings.method == method_t::Barrier) { | ||
| return run_barrier(problem, settings, timer); | ||
| } else if (settings.method == method_t::Concurrent) { | ||
| return run_concurrent(problem, settings, timer, is_batch_mode); |
There was a problem hiding this comment.
Should we include primal simplex in concurrent?
| const i_t m = L0_.m; | ||
| // Scatter x into a dense workspace, compute U0 * x, gather back to sparse. | ||
| std::vector<f_t> x_dense; | ||
| x.to_dense(x_dense); |
There was a problem hiding this comment.
Is this intentional? why not just work with sparse version?
…Fix work limit in feasibility pump. Check for reduced cost violation before calling primal simplex. Check if we reduced number of integer infeasibilites when we hit a work limit
| iter, | ||
| work_estimate, | ||
| false); | ||
| if (primal_status == primal_status_t::OPTIMAL) { |
There was a problem hiding this comment.
Does the primal introduce perturbations? I've seen instances where you need to cycles from dual to primal then back to dual to clean up the perturbations from primal.
There was a problem hiding this comment.
Currently the primal does not introduce perturbations. But I will likely need to in order to handle degeneracy. So we'd probably have to cycle back to dual to remove those perturbations. We might also have modes in primal and dual simplex where perturbations are not allowed.
|
🔔 Hi @anandhkb @chris-maes, this pull request has had no activity for 7 days. Please update or let us know if it can be closed. Thank you! If this is an "epic" issue, then please add the "epic" label to this issue. |
…nt, etc. The bound-flipping ratio test is rewritten. Instead of a heap-based approach a coarse filter is used to increase the step-length by multiples of 10. This is followed by a bucket sort. Each bucket contains variables with the same Harris ratio. We start from the final bucket, and go backward, trying to find a variable that statisfies our pivot threshold (more than 1/10th the maximum pivot) and maximizes the step length. When Phase-I completes and there are many bounded variables with NONBASIC_FIXED status with a small reduced costs | z_j | < dual_tol. These variables can be put on either bound. So we try three different initial points: 1) Set these variables on their lower bounds 2) Use a heuristic that assumes we have a slack basis B and sets the variable to the bound that minimizes the column sum. 3) Use a heuristic that sets the variable on the bound with the smallest absolute value. We test each of these points and choose the one that improves over the default (lower bounds) with less primal infeasibilities and small sum of primal infeasibilites squared. We do pertubations differently: 1) We attempt to remove perturbations as a variable leaves the basis. 2) We add a perturbation to the cost of the entering variable when we take a degenerate step. This accumulates after many degenerate steps and when we have a refactorization results in a different y and thus different reduced costs z 3) We don't apply perturbations if we are close to optimal. 4) We call set_primal_variables_on_bound after applying perturbation. Below we show a table of the baseline cuOpt code, compared to v5 (this PR). The HiGHS times were taken from a faster machine. So HiGHS advantage is slightly exaggerated. But it still exists. Problem Baseline v5 HiGHS v5/HiGHS ------------------------------------------------------------------------- momentum1 0.69 0.73 300.00 0.00 var-smallemery-m6j6 0.66 0.74 300.00 0.00 supportcase42 0.52 0.77 36.20 0.02 neos-5114902-kasavu 87.42 23.31 300.00 0.08 neos-5049753-cuanza 7.69 3.23 29.85 0.11 supportcase12 4.68 4.30 37.11 0.12 proteindesign121hz512p9 0.91 0.48 2.04 0.24 proteindesign122trx11p8 0.64 0.31 1.26 0.25 roi5alpha10n8 1.26 3.29 11.90 0.28 supportcase22 2.04 1.11 3.97 0.28 supportcase18 0.06 0.04 0.12 0.33 neos-787933 0.07 0.07 0.18 0.39 supportcase7 1.29 1.46 3.52 0.41 rocII-5-11 0.09 0.09 0.21 0.43 30n20b8 0.08 0.05 0.11 0.45 neos-860300 0.10 0.07 0.15 0.47 neos-5093327-huahum 0.23 0.23 0.48 0.48 cryptanalysiskb128n5obj14 29.78 6.14 12.54 0.49 co-100 0.68 0.64 1.28 0.50 fhnw-binpack4-48 0.07 0.03 0.06 0.50 mzzv11 40.19 8.46 16.71 0.51 rd-rplusc-21 0.23 0.25 0.49 0.51 lectsched-5-obj 0.13 0.10 0.18 0.56 roi2alpha3n4 0.31 0.67 1.16 0.58 dws008-01 0.04 0.03 0.05 0.60 neos-5052403-cygnet 300.00 187.30 300.00 0.62 cvs16r128-89 0.94 1.08 1.72 0.63 cryptanalysiskb128n5obj16 29.53 6.53 10.27 0.64 neos-5195221-niemur 0.50 0.24 0.37 0.65 neos-5188808-nattai 0.30 0.19 0.29 0.66 neos-3004026-krka 0.07 0.08 0.12 0.67 thor50dday 0.27 0.25 0.37 0.68 neos-4300652-rahue 1.22 0.56 0.80 0.70 square47 79.88 89.39 126.92 0.70 n3div36 0.11 0.13 0.18 0.72 blp-ar98 0.10 0.08 0.11 0.73 neos-960392 8.93 1.97 2.66 0.74 tbfp-network 9.06 6.73 9.04 0.74 neos-4647030-tutaki 2.47 2.33 3.09 0.75 decomp2 0.19 0.10 0.12 0.83 wachplan 0.25 0.23 0.26 0.88 netdiversion 7.65 8.61 9.43 0.91 supportcase40 0.24 0.22 0.24 0.92 istanbul-no-cutoff 0.71 0.87 0.94 0.93 supportcase10 300.00 105.36 113.55 0.93 ns1760995 135.95 250.52 269.53 0.93 neos-5104907-jarama 124.70 88.17 89.64 0.98 h80x6320d 0.05 0.04 0.04 1.00 highschool1-aigio 300.00 300.00 300.00 1.00 neos-1456979 0.05 0.04 0.04 1.00 neos-3988577-wolgan 278.89 300.00 300.00 1.00 neos859080 0.01 0.01 0.01 1.00 physiciansched3-3 300.00 300.00 300.00 1.00 pk1 0.02 0.01 0.01 1.00 rail02 300.00 300.00 300.00 1.00 s100 300.00 300.00 300.00 1.00 savsched1 300.00 300.00 300.00 1.00 supportcase19 300.00 300.00 300.00 1.00 swath3 0.03 0.03 0.03 1.00 timtab1 0.01 0.01 0.01 1.00 traininstance2 0.09 0.04 0.04 1.00 traininstance6 0.04 0.03 0.03 1.00 neos-4532248-waihi 2.61 0.92 0.90 1.02 cod105 9.06 7.73 7.46 1.04 square41 28.57 42.48 39.86 1.07 comp21-2idx 1.55 0.38 0.35 1.09 neos-873061 1.46 1.51 1.38 1.09 blp-ic98 0.12 0.11 0.10 1.10 neos-3555904-turama 1.31 1.52 1.37 1.11 piperout-27 0.67 0.29 0.26 1.12 sp97ar 0.40 0.37 0.33 1.12 piperout-08 0.39 0.18 0.16 1.12 triptim1 71.42 58.41 51.43 1.14 bnatt500 0.29 0.16 0.14 1.14 mushroom-best 0.26 0.23 0.20 1.15 leo2 0.14 0.15 0.13 1.15 neos-848589 1.25 1.02 0.85 1.20 neos-1122047 2.09 1.96 1.61 1.22 germanrr 0.31 0.33 0.27 1.22 net12 0.55 0.43 0.35 1.23 drayage-100-23 0.07 0.05 0.04 1.25 neos-3381206-awhea 0.08 0.05 0.04 1.25 neos-4738912-atrato 0.05 0.05 0.04 1.25 uct-subprob 0.11 0.10 0.08 1.25 ns1644855 300.00 300.00 238.30 1.26 leo1 0.10 0.09 0.07 1.29 atlanta-ip 6.85 5.86 4.54 1.29 neos-5107597-kakapo 0.04 0.13 0.10 1.30 air05 0.28 0.24 0.18 1.33 swath1 0.04 0.04 0.03 1.33 sp98ar 0.39 0.42 0.31 1.35 radiationm18-12-05 0.23 0.19 0.14 1.36 bnatt400 0.16 0.11 0.08 1.38 sct2 0.22 0.20 0.14 1.43 mcsched 0.27 0.23 0.16 1.44 supportcase6 7.39 6.03 4.18 1.44 irp 0.11 0.13 0.09 1.44 neos-4722843-widden 1.17 2.25 1.54 1.46 ns1952667 8.95 1.18 0.80 1.47 hypothyroid-k1 4.36 4.47 3.01 1.49 rail507 7.17 4.22 2.84 1.49 neos-3402294-bobin 3.07 1.79 1.20 1.49 drayage-25-23 0.08 0.06 0.04 1.50 icir97_tension 0.03 0.03 0.02 1.50 n5-3 0.03 0.03 0.02 1.50 neos-1582420 0.12 0.09 0.06 1.50 rococoC10-001000 0.04 0.03 0.02 1.50 neos-3216931-puriri 7.33 4.89 3.23 1.51 fast0507 7.37 4.24 2.77 1.53 ns1116954 156.23 16.44 10.73 1.53 neos-4763324-toguru 8.32 7.78 5.00 1.56 neos-3402454-bohle 221.94 115.56 74.15 1.56 nexp-150-20-8-5 0.10 0.11 0.07 1.57 trento1 3.08 3.49 2.21 1.58 qap10 15.57 10.58 6.68 1.58 neos8 0.38 0.42 0.26 1.62 rmatr200-p5 7.50 7.64 4.61 1.66 cmflsp50-24-8-8 0.77 0.73 0.44 1.66 neos-3083819-nubu 0.06 0.05 0.03 1.67 ran14x18-disj-8 0.04 0.05 0.03 1.67 rocI-4-11 0.11 0.10 0.06 1.67 neos-2987310-joes 1.52 1.67 1.00 1.67 neos-662469 1.65 0.91 0.53 1.72 chromaticindex512-7 16.62 37.48 21.24 1.76 k1mushroom 31.00 30.56 16.80 1.82 rococoB10-011000 0.13 0.11 0.06 1.83 comp07-2idx 4.03 2.05 1.10 1.86 reblock115 0.16 0.17 0.09 1.89 opm2-z10-s4 89.23 85.57 44.75 1.91 neos-3024952-loue 0.41 0.39 0.20 1.95 neos-2746589-doon 7.53 5.95 3.02 1.97 50v-10 0.02 0.02 0.01 2.00 b1c1s1 0.05 0.04 0.02 2.00 bppc4-08 0.08 0.04 0.02 2.00 cost266-UUE 0.03 0.04 0.02 2.00 eil33-2 0.05 0.06 0.03 2.00 enlight_hard 0.02 0.02 0.01 2.00 exp-1-500-5-5 0.02 0.02 0.01 2.00 fhnw-binpack4-4 0.03 0.02 0.01 2.00 gen-ip002 0.02 0.02 0.01 2.00 gen-ip054 0.03 0.02 0.01 2.00 glass4 0.02 0.02 0.01 2.00 graphdraw-domain 0.03 0.02 0.01 2.00 mad 0.02 0.02 0.01 2.00 markshare2 0.02 0.02 0.01 2.00 markshare_4_0 0.02 0.02 0.01 2.00 mas74 0.03 0.02 0.01 2.00 mas76 0.02 0.02 0.01 2.00 neos-3046615-murg 0.02 0.02 0.01 2.00 neos-3754480-nidda 0.02 0.02 0.01 2.00 neos-4338804-snowy 0.03 0.02 0.01 2.00 neos-4954672-berkel 0.02 0.02 0.01 2.00 neos-911970 0.03 0.02 0.01 2.00 neos5 0.02 0.02 0.01 2.00 pg 0.03 0.02 0.01 2.00 sp150x300d 0.02 0.02 0.01 2.00 supportcase26 0.03 0.02 0.01 2.00 tr12-30 0.03 0.02 0.01 2.00 mzzv42z 10.10 1.91 0.95 2.01 radiationm40-10-02 1.58 1.44 0.71 2.03 supportcase33 0.99 1.12 0.55 2.04 eilA101-2 2.68 2.86 1.39 2.06 fiball 0.71 0.30 0.14 2.14 chromaticindex1024-7 45.86 201.94 93.65 2.16 roll3000 0.12 0.13 0.06 2.17 n2seq36q 0.46 0.53 0.24 2.21 nursesched-sprint02 0.38 0.51 0.23 2.22 nw04 0.44 1.38 0.61 2.26 physiciansched6-2 11.15 15.62 6.72 2.32 seymour 0.83 0.89 0.38 2.34 ns1830653 0.42 0.33 0.14 2.36 seymour1 0.83 0.91 0.38 2.39 rmatr100-p10 0.27 0.34 0.14 2.43 nursesched-medium-hint03 10.47 11.61 4.73 2.45 neos-1171448 2.35 2.08 0.84 2.48 splice1k1 21.62 22.71 9.17 2.48 neos-4387871-tavua 0.10 0.10 0.04 2.50 nu25-pr12 0.05 0.05 0.02 2.50 unitcal_7 0.96 1.00 0.39 2.56 glass-sc 0.31 0.31 0.12 2.58 buildingenergy 300.00 300.00 115.61 2.59 sing44 9.62 14.79 5.69 2.60 bab2 300.00 76.29 28.97 2.63 graph20-20-1rand 0.23 0.32 0.12 2.67 rail01 223.84 196.04 71.23 2.75 bab6 143.43 38.82 14.06 2.76 CMS750_4 0.38 0.39 0.14 2.79 map16715-04 13.26 19.01 6.80 2.80 neos-2978193-inde 0.16 0.14 0.05 2.80 sorrell3 1.07 1.13 0.40 2.82 assign1-5-8 0.03 0.03 0.01 3.00 binkar10_1 0.02 0.03 0.01 3.00 csched008 0.11 0.09 0.03 3.00 ic97_potential 0.02 0.03 0.01 3.00 lotsize 0.03 0.03 0.01 3.00 mik-250-20-75-4 0.02 0.03 0.01 3.00 neos-2657525-crna 0.03 0.03 0.01 3.00 neos-3627168-kasai 0.04 0.03 0.01 3.00 p200x1188c 0.03 0.03 0.01 3.00 pg5_34 0.03 0.03 0.01 3.00 sing326 9.55 14.16 4.69 3.02 cbs-cta 0.42 0.32 0.10 3.20 csched007 0.19 0.16 0.05 3.20 uccase9 11.54 17.21 5.20 3.31 map10 11.08 21.74 6.25 3.48 neos-1171737 0.70 0.70 0.20 3.50 fastxgemm-n2r6s0t2 0.18 0.29 0.08 3.62 neos-957323 300.00 28.38 7.74 3.67 neos-1445765 0.16 0.35 0.09 3.89 gmu-35-40 0.03 0.04 0.01 4.00 neos17 0.03 0.04 0.01 4.00 s250r10 300.00 300.00 71.74 4.18 neos-1354092 300.00 300.00 70.92 4.23 milo-v12-6-r2-40-1 0.25 0.22 0.05 4.40 ns1208400 3.96 1.60 0.36 4.44 app1-1 0.08 0.09 0.02 4.50 neos-950242 1.05 0.81 0.18 4.50 uccase12 72.32 6.47 1.38 4.69 peg-solitaire-a3 1.88 2.54 0.52 4.88 beasleyC3 0.05 0.05 0.01 5.00 gmu-35-50 0.05 0.05 0.01 5.00 irish-electricity 181.58 300.00 59.53 5.04 academictimetablesmall 14.89 4.15 0.82 5.06 ex10 300.00 300.00 59.25 5.06 dano3_3 46.88 96.86 19.06 5.08 dano3_5 46.79 96.97 19.03 5.10 gfd-schedulen180f7d50m30k18 80.00 39.02 6.81 5.73 neos-2075418-temuka 128.33 300.00 50.57 5.93 mc11 0.06 0.06 0.01 6.00 neos-933966 16.79 18.35 2.80 6.55 neos-827175 9.36 1.91 0.29 6.59 neos-3656078-kumeu 2.37 1.57 0.23 6.83 app1-2 5.03 5.33 0.70 7.61 snp-02-004-104 14.18 23.47 2.83 8.29 neos-4413714-turia 3.97 16.53 1.93 8.56 neos-631710 300.00 300.00 31.97 9.38 satellites2-40 29.70 125.33 10.51 11.92 brazil3 300.00 93.59 7.24 12.93 ex9 300.00 300.00 14.07 21.32 satellites2-60-fs 4.17 171.22 3.34 51.26 ------------------------------------------------------------------------- Geomean Baseline/v5: 1.1452 Shifted(+1s): 1.0759 Geomean v5/HiGHS: 1.5487 Shifted(+1s): 1.1823 (240 problems)
Have the bound-flipping ratio test return the set of variables that must
flip at its selected step length. Flip exactly those variables in
flip_bounds.
The BFRT piecewise-linear objective model changes a bounded variable's
bound when its reduced cost crosses zero. Flipping at zero is numerically
unstable: small reduced-cost changes between iterations can move a
variable repeatedly across zero, reverse its bound, and cause cycling.
Use dual_tol / 10 to decide which variables to flip, leaving a small dead
zone around zero. flip_bounds flips exactly these variables (previously
it decided which bounds to flip using a separate mismatched tolerance).
Within that dead zone, the objective represented by the BFRT model may
differ from the objective computed using NONBASIC_UPPER/NONBASIC_LOWER.
For a bounded variable this discrepancy is at most
(upper_j - lower_j) * dual_tol / 10
and the total discrepancy is bounded by the sum of this quantity over
the unflipped variables.
Also stop the BFRT coarse-filter search after it has scanned all
breakpoints. Without the scan_start < num_breakpoints condition,
physiciansched3-3 loops forever when coarse_threshold remains zero after
all candidates have already been processed.
On the 240 MIPLIB LP relaxations, this change prevents five 300-second
timeouts (neos-5052403-cygnet, physiciansched3-3, supportcase10, bab2,
and brazil3), but introduces two new 300-second timeouts
(neos-3988577-wolgan and neos-2075418-temuka).
Unsolved runs are capped at 300 seconds. The presolve-only
neos-787933 uses its presolve time.
The HiGHS times were taken from a faster machine. So the HiGHS advantage
is slightly exaggerated. But it still exists.
Problem Baseline v5 v8 HiGHS v8/HiGHS
------------------------------------------------------------------------------------
momentum1 0.69 0.73 0.70 300.00 0.00
var-smallemery-m6j6 0.66 0.74 0.72 300.00 0.00
neos-5114902-kasavu 87.40 23.29 4.91 300.00 0.02
supportcase42 0.52 0.77 0.78 36.20 0.02
neos-5049753-cuanza 7.68 3.22 1.91 29.85 0.06
supportcase12 4.68 4.30 4.72 37.11 0.13
proteindesign121hz512p9 0.91 0.48 0.37 2.04 0.18
supportcase22 2.03 1.11 0.73 3.97 0.18
roi2alpha3n4 0.31 0.67 0.22 1.16 0.19
proteindesign122trx11p8 0.64 0.31 0.26 1.26 0.21
mzzv11 40.19 8.46 3.45 16.71 0.21
supportcase18 0.06 0.04 0.03 0.12 0.25
roi5alpha10n8 1.25 3.29 3.42 11.90 0.29
neos-5052403-cygnet 300.00 187.30 110.25 300.00 0.37
supportcase7 1.29 1.46 1.36 3.52 0.39
rd-rplusc-21 0.23 0.25 0.19 0.49 0.39
neos-787933 0.06 0.07 0.07 0.18 0.39
ns1760995 135.94 250.52 114.63 269.53 0.43
rocII-5-11 0.09 0.09 0.10 0.21 0.48
fhnw-binpack4-48 0.07 0.03 0.03 0.06 0.50
neos-5093327-huahum 0.23 0.23 0.25 0.48 0.52
30n20b8 0.08 0.05 0.06 0.11 0.55
lectsched-5-obj 0.13 0.10 0.10 0.18 0.56
neos-4647030-tutaki 2.47 2.33 1.72 3.09 0.56
physiciansched6-2 11.15 15.62 3.84 6.72 0.57
neos-3004026-krka 0.06 0.08 0.07 0.12 0.58
co-100 0.68 0.64 0.75 1.28 0.59
neos-5104907-jarama 124.69 88.16 52.83 89.64 0.59
neos-3402454-bohle 221.86 115.49 44.09 74.15 0.59
dws008-01 0.04 0.03 0.03 0.05 0.60
n3div36 0.11 0.13 0.11 0.18 0.61
neos-5188808-nattai 0.30 0.19 0.18 0.29 0.62
cvs16r128-89 0.93 1.08 1.13 1.72 0.66
neos-860300 0.10 0.07 0.10 0.15 0.67
neos-4300652-rahue 1.22 0.56 0.54 0.80 0.68
cryptanalysiskb128n5obj14 29.78 6.14 9.28 12.54 0.74
square47 79.87 89.37 95.31 126.92 0.75
istanbul-no-cutoff 0.71 0.87 0.71 0.94 0.76
thor50dday 0.27 0.25 0.28 0.37 0.76
ns1952667 8.95 1.18 0.62 0.80 0.77
neos-5195221-niemur 0.50 0.24 0.30 0.37 0.81
tbfp-network 9.06 6.73 7.80 9.04 0.86
cryptanalysiskb128n5obj16 29.53 6.53 8.93 10.27 0.87
blp-ic98 0.12 0.11 0.09 0.10 0.90
neos-3555904-turama 1.31 1.52 1.29 1.37 0.94
physiciansched3-3 300.00 300.00 288.13 300.00 0.96
comp21-2idx 1.55 0.38 0.34 0.35 0.97
square41 28.57 42.48 39.17 39.86 0.98
blp-ar98 0.10 0.08 0.11 0.11 1.00
decomp2 0.19 0.10 0.12 0.12 1.00
highschool1-aigio 300.00 300.00 300.00 300.00 1.00
leo2 0.13 0.15 0.13 0.13 1.00
neos-3988577-wolgan 278.89 300.00 300.00 300.00 1.00
neos-4738912-atrato 0.05 0.05 0.04 0.04 1.00
neos8 0.38 0.42 0.26 0.26 1.00
rail02 300.00 300.00 300.00 300.00 1.00
s100 300.00 300.00 300.00 300.00 1.00
savsched1 300.00 300.00 300.00 300.00 1.00
supportcase19 300.00 300.00 300.00 300.00 1.00
swath3 0.03 0.03 0.03 0.03 1.00
traininstance2 0.09 0.04 0.04 0.04 1.00
traininstance6 0.04 0.03 0.03 0.03 1.00
piperout-08 0.39 0.18 0.17 0.16 1.06
neos-873061 1.46 1.51 1.47 1.38 1.07
sct2 0.22 0.20 0.15 0.14 1.07
piperout-27 0.67 0.29 0.28 0.26 1.08
neos-4532248-waihi 2.61 0.92 0.97 0.90 1.08
neos-3216931-puriri 7.33 4.89 3.50 3.23 1.08
academictimetablesmall 14.89 4.15 0.90 0.82 1.10
germanrr 0.31 0.33 0.30 0.27 1.11
nursesched-sprint02 0.38 0.51 0.26 0.23 1.13
triptim1 71.42 58.41 58.50 51.43 1.14
neos-848589 1.24 1.01 0.97 0.85 1.14
cod105 9.06 7.73 8.56 7.46 1.15
supportcase10 300.00 105.36 133.84 113.55 1.18
sp97ar 0.40 0.37 0.39 0.33 1.18
wachplan 0.25 0.23 0.31 0.26 1.19
sp98ar 0.39 0.41 0.37 0.31 1.19
supportcase40 0.24 0.22 0.29 0.24 1.21
neos-960392 8.93 1.97 3.30 2.66 1.24
drayage-25-23 0.08 0.06 0.05 0.04 1.25
h80x6320d 0.05 0.04 0.05 0.04 1.25
neos-3381206-awhea 0.08 0.05 0.05 0.04 1.25
ns1644855 300.00 300.00 300.00 238.30 1.26
nursesched-medium-hint03 10.47 11.61 6.00 4.73 1.27
radiationm18-12-05 0.23 0.19 0.18 0.14 1.29
mushroom-best 0.26 0.23 0.26 0.20 1.30
supportcase6 7.39 6.03 5.52 4.18 1.32
swath1 0.04 0.04 0.04 0.03 1.33
comp07-2idx 4.03 2.05 1.47 1.10 1.34
neos-2746589-doon 7.53 5.95 4.11 3.02 1.36
neos-5107597-kakapo 0.04 0.13 0.14 0.10 1.40
net12 0.55 0.43 0.50 0.35 1.43
neos-3402294-bobin 3.07 1.79 1.72 1.20 1.43
neos-4722843-widden 1.17 2.25 2.21 1.54 1.44
irp 0.11 0.13 0.13 0.09 1.44
supportcase33 0.99 1.12 0.80 0.55 1.45
fast0507 7.36 4.24 4.05 2.77 1.46
mzzv42z 10.10 1.91 1.42 0.95 1.49
bnatt500 0.29 0.16 0.21 0.14 1.50
drayage-100-23 0.07 0.05 0.06 0.04 1.50
icir97_tension 0.03 0.03 0.03 0.02 1.50
neos-1582420 0.12 0.09 0.09 0.06 1.50
rococoC10-001000 0.04 0.03 0.03 0.02 1.50
air05 0.28 0.24 0.27 0.18 1.50
neos-1122047 2.09 1.96 2.43 1.61 1.51
nexp-150-20-8-5 0.10 0.11 0.11 0.07 1.57
neos-2978193-inde 0.16 0.14 0.08 0.05 1.60
rail507 7.17 4.22 4.64 2.84 1.63
hypothyroid-k1 4.36 4.47 4.93 3.01 1.64
neos-3083819-nubu 0.06 0.05 0.05 0.03 1.67
ran14x18-disj-8 0.04 0.05 0.05 0.03 1.67
cmflsp50-24-8-8 0.77 0.73 0.74 0.44 1.68
leo1 0.10 0.09 0.12 0.07 1.71
trento1 3.08 3.49 3.80 2.21 1.72
neos-662469 1.65 0.91 0.93 0.53 1.75
rmatr200-p5 7.50 7.63 8.13 4.61 1.76
atlanta-ip 6.85 5.86 8.14 4.54 1.79
rocI-4-11 0.11 0.10 0.11 0.06 1.83
mcsched 0.27 0.23 0.30 0.16 1.88
chromaticindex512-7 16.62 37.48 40.10 21.24 1.89
neos-3024952-loue 0.41 0.39 0.38 0.20 1.90
neos-2987310-joes 1.52 1.67 1.91 1.00 1.91
netdiversion 7.64 8.61 18.16 9.43 1.93
qap10 15.57 10.58 12.87 6.68 1.93
sing44 9.62 14.79 11.01 5.69 1.93
k1mushroom 31.00 30.55 32.65 16.80 1.94
opm2-z10-s4 89.23 85.57 87.53 44.75 1.96
neos-4763324-toguru 8.32 7.78 9.86 5.00 1.97
50v-10 0.02 0.02 0.02 0.01 2.00
bnatt400 0.16 0.11 0.16 0.08 2.00
eil33-2 0.05 0.06 0.06 0.03 2.00
enlight_hard 0.02 0.02 0.02 0.01 2.00
exp-1-500-5-5 0.02 0.02 0.02 0.01 2.00
fhnw-binpack4-4 0.03 0.02 0.02 0.01 2.00
gen-ip002 0.02 0.02 0.02 0.01 2.00
gen-ip054 0.03 0.02 0.02 0.01 2.00
glass4 0.02 0.02 0.02 0.01 2.00
mad 0.02 0.02 0.02 0.01 2.00
markshare2 0.02 0.02 0.02 0.01 2.00
markshare_4_0 0.02 0.02 0.02 0.01 2.00
mas74 0.03 0.02 0.02 0.01 2.00
mas76 0.02 0.02 0.02 0.01 2.00
mik-250-20-75-4 0.02 0.03 0.02 0.01 2.00
neos-1456979 0.05 0.04 0.08 0.04 2.00
neos-3046615-murg 0.02 0.02 0.02 0.01 2.00
neos-3754480-nidda 0.02 0.02 0.02 0.01 2.00
neos-4338804-snowy 0.03 0.02 0.02 0.01 2.00
neos-911970 0.03 0.02 0.02 0.01 2.00
neos5 0.02 0.02 0.02 0.01 2.00
neos859080 0.01 0.01 0.02 0.01 2.00
nu25-pr12 0.05 0.05 0.04 0.02 2.00
pg 0.03 0.02 0.02 0.01 2.00
pg5_34 0.03 0.03 0.02 0.01 2.00
pk1 0.02 0.01 0.02 0.01 2.00
sp150x300d 0.02 0.02 0.02 0.01 2.00
supportcase26 0.03 0.02 0.02 0.01 2.00
timtab1 0.01 0.01 0.02 0.01 2.00
radiationm40-10-02 1.58 1.44 1.45 0.71 2.04
ns1830653 0.42 0.33 0.29 0.14 2.07
uct-subprob 0.11 0.10 0.17 0.08 2.12
neos-957323 300.00 28.38 16.74 7.74 2.16
roll3000 0.12 0.13 0.13 0.06 2.17
dano3_3 46.88 96.86 42.60 19.06 2.24
dano3_5 46.79 96.97 42.62 19.03 2.24
neos-4387871-tavua 0.10 0.10 0.09 0.04 2.25
rmatr100-p10 0.27 0.34 0.32 0.14 2.29
unitcal_7 0.96 1.00 0.91 0.39 2.33
ns1116954 156.23 16.44 25.16 10.73 2.34
rail01 223.84 196.04 167.23 71.23 2.35
chromaticindex1024-7 45.86 201.94 220.63 93.65 2.36
csched007 0.19 0.16 0.12 0.05 2.40
eilA101-2 2.68 2.86 3.35 1.39 2.41
bab2 300.00 76.29 70.29 28.97 2.43
reblock115 0.16 0.17 0.22 0.09 2.44
seymour1 0.83 0.91 0.94 0.38 2.47
b1c1s1 0.05 0.04 0.05 0.02 2.50
cost266-UUE 0.03 0.04 0.05 0.02 2.50
n5-3 0.03 0.03 0.05 0.02 2.50
rococoB10-011000 0.13 0.11 0.15 0.06 2.50
uccase9 11.54 17.20 13.22 5.20 2.54
seymour 0.83 0.89 0.98 0.38 2.58
buildingenergy 300.00 300.00 300.00 115.61 2.59
splice1k1 21.62 22.71 23.93 9.17 2.61
sing326 9.54 14.16 12.30 4.69 2.62
neos-1171737 0.70 0.70 0.54 0.20 2.70
satellites2-60-fs 4.17 171.22 9.13 3.34 2.73
glass-sc 0.31 0.31 0.34 0.12 2.83
gfd-schedulen180f7d50m30k18 80.00 39.02 19.47 6.81 2.86
nw04 0.44 1.38 1.75 0.61 2.87
neos-1171448 2.35 2.08 2.41 0.84 2.87
binkar10_1 0.02 0.03 0.03 0.01 3.00
bppc4-08 0.08 0.04 0.06 0.02 3.00
csched008 0.11 0.09 0.09 0.03 3.00
graph20-20-1rand 0.23 0.32 0.36 0.12 3.00
graphdraw-domain 0.02 0.02 0.03 0.01 3.00
ic97_potential 0.02 0.03 0.03 0.01 3.00
neos-2657525-crna 0.03 0.03 0.03 0.01 3.00
neos-4954672-berkel 0.02 0.02 0.03 0.01 3.00
neos17 0.03 0.04 0.03 0.01 3.00
p200x1188c 0.03 0.03 0.03 0.01 3.00
tr12-30 0.03 0.02 0.03 0.01 3.00
map16715-04 13.26 19.01 20.42 6.80 3.00
CMS750_4 0.38 0.39 0.43 0.14 3.07
irish-electricity 181.58 300.00 183.12 59.53 3.08
n2seq36q 0.46 0.53 0.74 0.24 3.08
sorrell3 1.07 1.13 1.25 0.40 3.12
map10 11.08 21.74 19.82 6.25 3.17
bab6 143.43 38.82 44.66 14.06 3.18
ns1208400 3.96 1.60 1.17 0.36 3.25
neos-933966 16.79 18.35 9.31 2.80 3.33
neos-1445765 0.16 0.35 0.30 0.09 3.33
uccase12 72.32 6.47 4.77 1.38 3.46
neos-950242 1.05 0.81 0.66 0.18 3.67
assign1-5-8 0.03 0.03 0.04 0.01 4.00
gmu-35-40 0.03 0.04 0.04 0.01 4.00
gmu-35-50 0.05 0.05 0.04 0.01 4.00
lotsize 0.03 0.03 0.04 0.01 4.00
neos-3627168-kasai 0.04 0.03 0.04 0.01 4.00
s250r10 300.00 300.00 300.00 71.74 4.18
neos-1354092 300.00 300.00 300.00 70.92 4.23
fastxgemm-n2r6s0t2 0.18 0.29 0.34 0.08 4.25
milo-v12-6-r2-40-1 0.25 0.22 0.22 0.05 4.40
app1-1 0.08 0.09 0.09 0.02 4.50
fiball 0.71 0.30 0.66 0.14 4.71
cbs-cta 0.42 0.32 0.48 0.10 4.80
beasleyC3 0.05 0.05 0.05 0.01 5.00
mc11 0.06 0.06 0.05 0.01 5.00
ex10 300.00 300.00 300.00 59.25 5.06
peg-solitaire-a3 1.88 2.54 2.81 0.52 5.40
neos-3656078-kumeu 2.37 1.57 1.27 0.23 5.52
neos-827175 9.36 1.91 1.66 0.29 5.72
neos-2075418-temuka 128.30 300.00 300.00 50.57 5.93
snp-02-004-104 14.17 23.47 19.21 2.83 6.79
app1-2 5.03 5.33 5.26 0.70 7.51
neos-631710 300.00 300.00 300.00 31.97 9.38
neos-4413714-turia 3.97 16.53 20.16 1.93 10.45
brazil3 300.00 93.59 79.55 7.24 10.99
satellites2-40 29.70 125.33 186.43 10.51 17.74
ex9 300.00 300.00 300.00 14.07 21.32
Geomean Baseline/v8: 1.19 Shifted(+1s): 1.14
Geomean v5/v8: 1.05 Shifted(+1s): 1.06
Geomean v8/HiGHS: 1.48 Shifted(+1s): 1.12
(240 problems)
…computation to try to remain dual feasible
…_out_integer_variables in the tree
Signed-off-by: Christopher Maes <cmaes@nvidia.com>
These improvements were discovered through Hiverge's automated exploration
of changes to cuOpt's dual simplex solver and then isolated and validated
independently against the v8 baseline.
Apply row equilibration to imbalanced linear programs before the existing
column normalization. For each row, divide the matrix coefficients and
right-hand side by the row infinity norm when the maximum-to-minimum row
norm ratio exceeds 10.
Restrict this equilibration to standalone linear programs. MIP already
performs integer-aware row scaling before presolve, and QP and SOCP
problems use the existing iterative Ruiz equilibration path.
Increase the initial objective perturbation from
5e-7 * max_abs_objective
to
1e-5 * max_abs_objective
when more than 5% of the nonbasic variables are dual degenerate. Retain
the original perturbation strength for other problems.
The stronger perturbation separates coincident and nearly coincident
reduced costs. Across the benchmark it reduces the BFRT zero-step rate
from 34.0% with row scaling alone to 15.4%. Row equilibration improves
the numerical conditioning of models with imbalanced constraint rows
and prevents several expensive or failed cleanup trajectories.
On 240 MIPLIB LP relaxations, v9 improves the raw runtime geometric mean
by 1.1865x relative to v8 and the one-second-shifted geometric mean by
1.1037x. It solves 229 problems within 300 seconds, compared with 226
for v8.
The HiGHS times were taken from a faster machine, so the apparent HiGHS
advantage is slightly exaggerated.
Problem v8 v9 Baseline HiGHS v9/HiGHS
------------------------------------------------------------------------------------
momentum1 0.70 0.69 0.69 300.00 0.00
var-smallemery-m6j6 0.72 0.69 0.66 300.00 0.00
neos-5114902-kasavu 4.91 2.74 87.40 300.00 0.01
supportcase42 0.78 0.80 0.52 36.20 0.02
neos-5049753-cuanza 1.91 1.02 7.68 29.85 0.03
roi5alpha10n8 3.42 1.35 1.25 11.90 0.11
mzzv11 3.45 2.40 40.19 16.71 0.14
supportcase12 4.72 6.35 4.68 37.11 0.17
roi2alpha3n4 0.22 0.22 0.31 1.16 0.19
co-100 0.75 0.28 0.68 1.28 0.22
proteindesign121hz512p9 0.37 0.46 0.91 2.04 0.23
neos-5104907-jarama 52.83 21.53 124.69 89.64 0.24
supportcase18 0.03 0.03 0.06 0.12 0.25
ns1644855 300.00 63.39 300.00 238.30 0.27
proteindesign122trx11p8 0.26 0.35 0.64 1.26 0.28
neos-1354092 300.00 20.05 300.00 70.92 0.28
ns1952667 0.62 0.26 8.95 0.80 0.33
rd-rplusc-21 0.19 0.16 0.23 0.49 0.33
neos-787933 0.07 0.06 0.06 0.18 0.33
neos-5052403-cygnet 110.25 109.39 300.00 300.00 0.36
neos-860300 0.10 0.06 0.10 0.15 0.40
ns1760995 114.63 114.33 135.94 269.53 0.42
sct2 0.15 0.06 0.22 0.14 0.43
rocII-5-11 0.10 0.10 0.09 0.21 0.48
supportcase7 1.36 1.68 1.29 3.52 0.48
neos-5093327-huahum 0.25 0.23 0.23 0.48 0.48
satellites2-40 186.43 5.40 29.70 10.51 0.51
physiciansched6-2 3.84 3.60 11.15 6.72 0.54
neos-4647030-tutaki 1.72 1.68 2.47 3.09 0.54
n3div36 0.11 0.10 0.11 0.18 0.56
supportcase22 0.73 2.28 2.03 3.97 0.57
neos-4300652-rahue 0.54 0.46 1.22 0.80 0.57
neos-3004026-krka 0.07 0.07 0.06 0.12 0.58
neos-5107597-kakapo 0.14 0.06 0.04 0.10 0.60
lectsched-5-obj 0.10 0.11 0.13 0.18 0.61
neos-5188808-nattai 0.18 0.18 0.30 0.29 0.62
cvs16r128-89 1.13 1.07 0.93 1.72 0.62
30n20b8 0.06 0.07 0.08 0.11 0.64
blp-ar98 0.11 0.07 0.10 0.11 0.64
decomp2 0.12 0.08 0.19 0.12 0.67
swath3 0.03 0.02 0.03 0.03 0.67
neos-1171448 2.41 0.58 2.35 0.84 0.69
wachplan 0.31 0.18 0.25 0.26 0.69
neos-5195221-niemur 0.30 0.26 0.50 0.37 0.70
thor50dday 0.28 0.26 0.27 0.37 0.70
supportcase10 133.84 79.85 300.00 113.55 0.70
supportcase33 0.80 0.40 0.99 0.55 0.73
square47 95.31 93.64 79.87 126.92 0.74
neos-3381206-awhea 0.05 0.03 0.08 0.04 0.75
buildingenergy 300.00 87.82 300.00 115.61 0.76
cryptanalysiskb128n5obj14 9.28 9.55 29.78 12.54 0.76
neos-2746589-doon 4.11 2.36 7.53 3.02 0.78
nursesched-medium-hint03 6.00 3.71 10.47 4.73 0.78
blp-ic98 0.09 0.08 0.12 0.10 0.80
neos-848589 0.97 0.70 1.24 0.85 0.82
dano3_3 42.60 16.02 46.88 19.06 0.84
dano3_5 42.62 16.04 46.79 19.03 0.84
neos-1171737 0.54 0.17 0.70 0.20 0.85
academictimetablesmall 0.90 0.70 14.89 0.82 0.85
tbfp-network 7.80 7.72 9.06 9.04 0.85
ns1116954 25.16 9.20 156.23 10.73 0.86
neos-3402454-bohle 44.09 64.41 221.86 74.15 0.87
comp21-2idx 0.34 0.31 1.55 0.35 0.89
leo2 0.13 0.12 0.13 0.13 0.92
radiationm18-12-05 0.18 0.13 0.23 0.14 0.93
neos-3555904-turama 1.29 1.31 1.31 1.37 0.96
mzzv42z 1.42 0.91 10.10 0.95 0.96
square41 39.17 38.25 28.57 39.86 0.96
cryptanalysiskb128n5obj16 8.93 10.05 29.53 10.27 0.98
drayage-100-23 0.06 0.04 0.07 0.04 1.00
dws008-01 0.03 0.05 0.04 0.05 1.00
h80x6320d 0.05 0.04 0.05 0.04 1.00
highschool1-aigio 300.00 300.00 300.00 300.00 1.00
icir97_tension 0.03 0.02 0.03 0.02 1.00
leo1 0.12 0.07 0.10 0.07 1.00
neos-3988577-wolgan 300.00 300.00 278.89 300.00 1.00
neos-4738912-atrato 0.04 0.04 0.05 0.04 1.00
neos8 0.26 0.26 0.38 0.26 1.00
nursesched-sprint02 0.26 0.23 0.38 0.23 1.00
physiciansched3-3 288.13 300.00 300.00 300.00 1.00
rail02 300.00 300.00 300.00 300.00 1.00
s100 300.00 300.00 300.00 300.00 1.00
savsched1 300.00 300.00 300.00 300.00 1.00
supportcase19 300.00 300.00 300.00 300.00 1.00
swath1 0.04 0.03 0.04 0.03 1.00
traininstance2 0.04 0.04 0.09 0.04 1.00
s250r10 300.00 71.91 300.00 71.74 1.00
neos-873061 1.47 1.46 1.46 1.38 1.06
fiball 0.66 0.15 0.71 0.14 1.07
neos-3402294-bobin 1.72 1.29 3.07 1.20 1.08
neos-4413714-turia 20.16 2.10 3.97 1.93 1.09
sp98ar 0.37 0.34 0.39 0.31 1.10
germanrr 0.30 0.30 0.31 0.27 1.11
cod105 8.56 8.39 9.06 7.46 1.12
comp07-2idx 1.47 1.26 4.03 1.10 1.15
neos-1122047 2.43 1.87 2.09 1.61 1.16
neos-4763324-toguru 9.86 5.82 8.32 5.00 1.16
supportcase6 5.52 4.93 7.39 4.18 1.18
supportcase40 0.29 0.29 0.24 0.24 1.21
neos-960392 3.30 3.29 8.93 2.66 1.24
drayage-25-23 0.05 0.05 0.08 0.04 1.25
neos-1456979 0.08 0.05 0.05 0.04 1.25
neos-3216931-puriri 3.50 4.20 7.33 3.23 1.30
cmflsp50-24-8-8 0.74 0.58 0.77 0.44 1.32
irp 0.13 0.12 0.11 0.09 1.33
sp97ar 0.39 0.44 0.40 0.33 1.33
neos-1582420 0.09 0.08 0.12 0.06 1.33
traininstance6 0.03 0.04 0.04 0.03 1.33
radiationm40-10-02 1.45 0.97 1.58 0.71 1.37
map10 19.82 8.54 11.08 6.25 1.37
cbs-cta 0.48 0.14 0.42 0.10 1.40
mushroom-best 0.26 0.28 0.26 0.20 1.40
fast0507 4.05 3.95 7.36 2.77 1.43
bnatt500 0.21 0.20 0.29 0.14 1.43
nexp-150-20-8-5 0.11 0.10 0.10 0.07 1.43
map16715-04 20.42 9.87 13.26 6.80 1.45
qap10 12.87 9.80 15.57 6.68 1.47
hypothyroid-k1 4.93 4.42 4.36 3.01 1.47
fhnw-binpack4-48 0.03 0.09 0.07 0.06 1.50
rocI-4-11 0.11 0.09 0.11 0.06 1.50
uct-subprob 0.17 0.12 0.11 0.08 1.50
neos-827175 1.66 0.44 9.36 0.29 1.52
rail507 4.64 4.37 7.17 2.84 1.54
uccase9 13.22 8.04 11.54 5.20 1.55
neos-4532248-waihi 0.97 1.40 2.61 0.90 1.56
reblock115 0.22 0.14 0.16 0.09 1.56
neos-957323 16.74 12.14 300.00 7.74 1.57
trento1 3.80 3.50 3.08 2.21 1.58
neos-2987310-joes 1.91 1.59 1.52 1.00 1.59
neos-3024952-loue 0.38 0.32 0.41 0.20 1.60
air05 0.27 0.29 0.28 0.18 1.61
neos-662469 0.93 0.86 1.65 0.53 1.62
bnatt400 0.16 0.13 0.16 0.08 1.62
neos-3083819-nubu 0.05 0.05 0.06 0.03 1.67
ran14x18-disj-8 0.05 0.05 0.04 0.03 1.67
roll3000 0.13 0.10 0.12 0.06 1.67
k1mushroom 32.65 28.33 31.00 16.80 1.69
opm2-z10-s4 87.53 75.72 89.23 44.75 1.69
istanbul-no-cutoff 0.71 1.63 0.71 0.94 1.73
neos-3656078-kumeu 1.27 0.40 2.37 0.23 1.74
rmatr200-p5 8.13 8.10 7.50 4.61 1.76
ns1830653 0.29 0.25 0.42 0.14 1.79
neos-2978193-inde 0.08 0.09 0.16 0.05 1.80
irish-electricity 183.12 109.17 181.58 59.53 1.83
mcsched 0.30 0.30 0.27 0.16 1.88
sing326 12.30 9.33 9.54 4.69 1.99
assign1-5-8 0.04 0.02 0.03 0.01 2.00
bppc4-08 0.06 0.04 0.08 0.02 2.00
eil33-2 0.06 0.06 0.05 0.03 2.00
ic97_potential 0.03 0.02 0.02 0.01 2.00
mik-250-20-75-4 0.02 0.02 0.02 0.01 2.00
neos-4338804-snowy 0.02 0.02 0.03 0.01 2.00
neos-4387871-tavua 0.09 0.08 0.10 0.04 2.00
neos-4954672-berkel 0.03 0.02 0.02 0.01 2.00
nu25-pr12 0.04 0.04 0.05 0.02 2.00
pg 0.02 0.02 0.03 0.01 2.00
tr12-30 0.03 0.02 0.03 0.01 2.00
satellites2-60-fs 9.13 6.89 4.17 3.34 2.06
ns1208400 1.17 0.76 3.96 0.36 2.11
splice1k1 23.93 19.92 21.62 9.17 2.17
sing44 11.01 12.54 9.62 5.69 2.20
gfd-schedulen180f7d50m30k18 19.47 15.81 80.00 6.81 2.32
rmatr100-p10 0.32 0.33 0.27 0.14 2.36
eilA101-2 3.35 3.29 2.68 1.39 2.37
netdiversion 18.16 22.67 7.64 9.43 2.40
bab6 44.66 34.02 143.43 14.06 2.42
chromaticindex512-7 40.10 53.03 16.62 21.24 2.50
graph20-20-1rand 0.36 0.30 0.23 0.12 2.50
n5-3 0.05 0.05 0.03 0.02 2.50
rococoB10-011000 0.15 0.15 0.13 0.06 2.50
seymour 0.98 0.95 0.83 0.38 2.50
seymour1 0.94 0.97 0.83 0.38 2.55
neos-950242 0.66 0.46 1.05 0.18 2.56
piperout-08 0.17 0.41 0.39 0.16 2.56
triptim1 58.50 132.50 71.42 51.43 2.58
bab2 70.29 75.19 300.00 28.97 2.60
atlanta-ip 8.14 12.20 6.85 4.54 2.69
unitcal_7 0.91 1.05 0.96 0.39 2.69
net12 0.50 0.96 0.55 0.35 2.74
glass-sc 0.34 0.33 0.31 0.12 2.75
neos-1445765 0.30 0.25 0.16 0.09 2.78
chromaticindex1024-7 220.63 262.17 45.86 93.65 2.80
nw04 1.75 1.71 0.44 0.61 2.80
sorrell3 1.25 1.14 1.07 0.40 2.85
n2seq36q 0.74 0.71 0.46 0.24 2.96
50v-10 0.02 0.03 0.02 0.01 3.00
b1c1s1 0.05 0.06 0.05 0.02 3.00
binkar10_1 0.03 0.03 0.02 0.01 3.00
cost266-UUE 0.05 0.06 0.03 0.02 3.00
fhnw-binpack4-4 0.02 0.03 0.03 0.01 3.00
gmu-35-40 0.04 0.03 0.03 0.01 3.00
lotsize 0.04 0.03 0.03 0.01 3.00
neos17 0.03 0.03 0.03 0.01 3.00
pg5_34 0.02 0.03 0.03 0.01 3.00
rococoC10-001000 0.03 0.06 0.04 0.02 3.00
csched007 0.12 0.16 0.19 0.05 3.20
neos-933966 9.31 9.15 16.79 2.80 3.27
milo-v12-6-r2-40-1 0.22 0.18 0.25 0.05 3.60
gmu-35-50 0.04 0.04 0.05 0.01 4.00
neos-3627168-kasai 0.04 0.04 0.04 0.01 4.00
p200x1188c 0.03 0.04 0.03 0.01 4.00
peg-solitaire-a3 2.81 2.14 1.88 0.52 4.12
uccase12 4.77 5.76 72.32 1.38 4.17
rail01 167.23 300.00 223.84 71.23 4.21
csched008 0.09 0.13 0.11 0.03 4.33
CMS750_4 0.43 0.63 0.38 0.14 4.50
piperout-27 0.28 1.23 0.67 0.26 4.73
neos-4722843-widden 2.21 7.67 1.17 1.54 4.98
app1-1 0.09 0.10 0.08 0.02 5.00
fastxgemm-n2r6s0t2 0.34 0.40 0.18 0.08 5.00
ex10 300.00 300.00 300.00 59.25 5.06
neos-2075418-temuka 300.00 300.00 128.30 50.57 5.93
beasleyC3 0.05 0.06 0.05 0.01 6.00
neos-631710 300.00 215.36 300.00 31.97 6.74
mc11 0.05 0.08 0.06 0.01 8.00
app1-2 5.26 5.77 5.03 0.70 8.24
snp-02-004-104 19.21 24.10 14.17 2.83 8.52
brazil3 79.55 61.89 300.00 7.24 8.55
gen-ip002 0.02 0.01 0.02 0.00 10.00
markshare2 0.02 0.01 0.02 0.00 10.00
markshare_4_0 0.02 0.01 0.02 0.00 10.00
mas74 0.02 0.01 0.03 0.00 10.00
neos859080 0.02 0.01 0.01 0.00 10.00
pk1 0.02 0.01 0.02 0.00 10.00
enlight_hard 0.02 0.02 0.02 0.00 20.00
exp-1-500-5-5 0.02 0.02 0.02 0.00 20.00
gen-ip054 0.02 0.02 0.03 0.00 20.00
glass4 0.02 0.02 0.02 0.00 20.00
graphdraw-domain 0.03 0.02 0.02 0.00 20.00
mad 0.02 0.02 0.02 0.00 20.00
mas76 0.02 0.02 0.02 0.00 20.00
neos-3046615-murg 0.02 0.02 0.02 0.00 20.00
neos-3754480-nidda 0.02 0.02 0.02 0.00 20.00
neos5 0.02 0.02 0.02 0.00 20.00
sp150x300d 0.02 0.02 0.02 0.00 20.00
supportcase26 0.02 0.02 0.03 0.00 20.00
timtab1 0.02 0.02 0.01 0.00 20.00
ex9 300.00 300.00 300.00 14.07 21.32
neos-2657525-crna 0.03 0.03 0.03 0.00 30.00
neos-911970 0.02 0.03 0.03 0.00 30.00
Geomean v8/v9: 1.1865 Shifted(+1s): 1.1037
Geomean Baseline/v9: 1.4170 Shifted(+1s): 1.2561
Geomean v9/HiGHS: 1.5257 Shifted(+1s): 1.0133
(240 problems)
This improvement was discovered through Hiverge's automated exploration of changes to cuOpt's dual simplex solver and then isolated on top of the v9 row-equilibration and perturbation changes. The bound-flipping ratio test searches Harris buckets from the latest to the earliest so that it favors a longer dual step. Within the selected bucket, choose the candidate with the largest absolute pivot instead of the largest exact breakpoint ratio. Use the exact ratio only to break ties between equal pivots. The change recovers ex9 and neos-3988577-wolgan, which time out in v9, but introduces a timeout on irish-electricity after failed primal cleanup. The larger pivots reduce total BFRT zero steps by 22.8% and improve the aggregate benchmark despite that cleanup regression. Problem v9 v10 Baseline HiGHS v10/HiGHS ------------------------------------------------------------------------------------- var-smallemery-m6j6 0.69 0.69 0.66 300.00 0.00 momentum1 0.69 0.70 0.69 300.00 0.00 neos-5114902-kasavu 2.74 2.73 87.40 300.00 0.01 supportcase42 0.80 0.76 0.52 36.20 0.02 neos-5049753-cuanza 1.02 1.05 7.68 29.85 0.04 supportcase12 6.35 4.11 4.68 37.11 0.11 roi5alpha10n8 1.35 1.32 1.25 11.90 0.11 mzzv11 2.40 2.12 40.19 16.71 0.13 ns1760995 114.33 44.10 135.94 269.53 0.16 ns1952667 0.26 0.15 8.95 0.80 0.19 proteindesign121hz512p9 0.46 0.40 0.91 2.04 0.20 roi2alpha3n4 0.22 0.24 0.31 1.16 0.21 co-100 0.28 0.27 0.68 1.28 0.21 neos-5104907-jarama 21.53 19.35 124.69 89.64 0.22 neos-5052403-cygnet 109.39 72.96 300.00 300.00 0.24 proteindesign122trx11p8 0.35 0.33 0.64 1.26 0.26 neos-1354092 20.05 20.09 300.00 70.92 0.28 supportcase18 0.03 0.04 0.06 0.12 0.33 rd-rplusc-21 0.16 0.17 0.23 0.49 0.35 30n20b8 0.07 0.04 0.08 0.11 0.36 ns1644855 63.39 88.00 300.00 238.30 0.37 neos-787933 0.06 0.07 0.06 0.18 0.39 sct2 0.06 0.06 0.22 0.14 0.43 supportcase7 1.68 1.53 1.29 3.52 0.43 wachplan 0.18 0.12 0.25 0.26 0.46 neos-860300 0.06 0.07 0.10 0.15 0.47 rocII-5-11 0.10 0.10 0.09 0.21 0.48 neos-5093327-huahum 0.23 0.23 0.23 0.48 0.48 supportcase22 2.28 1.92 2.03 3.97 0.48 physiciansched6-2 3.60 3.25 11.15 6.72 0.48 neos-5107597-kakapo 0.06 0.05 0.04 0.10 0.50 cvs16r128-89 1.07 0.88 0.93 1.72 0.51 satellites2-40 5.40 5.56 29.70 10.51 0.53 neos-4647030-tutaki 1.68 1.69 2.47 3.09 0.55 lectsched-5-obj 0.11 0.10 0.13 0.18 0.56 supportcase10 79.85 66.38 300.00 113.55 0.58 buildingenergy 87.82 68.31 300.00 115.61 0.59 n3div36 0.10 0.11 0.11 0.18 0.61 neos-5188808-nattai 0.18 0.19 0.30 0.29 0.66 tbfp-network 7.72 6.01 9.06 9.04 0.66 neos-5195221-niemur 0.26 0.25 0.50 0.37 0.68 thor50dday 0.26 0.25 0.27 0.37 0.68 nursesched-medium-hint03 3.71 3.23 10.47 4.73 0.68 square47 93.64 87.67 79.87 126.92 0.69 cryptanalysiskb128n5obj14 9.55 8.73 29.78 12.54 0.70 ns1116954 9.20 7.52 156.23 10.73 0.70 neos-1171448 0.58 0.60 2.35 0.84 0.71 academictimetablesmall 0.70 0.59 14.89 0.82 0.72 neos-3402454-bohle 64.41 55.07 221.86 74.15 0.74 neos-2746589-doon 2.36 2.25 7.53 3.02 0.75 neos-4300652-rahue 0.46 0.60 1.22 0.80 0.75 neos-3004026-krka 0.07 0.09 0.06 0.12 0.75 neos-3381206-awhea 0.03 0.03 0.08 0.04 0.75 square41 38.25 31.75 28.57 39.86 0.80 blp-ic98 0.08 0.08 0.12 0.10 0.80 dws008-01 0.05 0.04 0.04 0.05 0.80 supportcase33 0.40 0.44 0.99 0.55 0.80 neos-848589 0.70 0.68 1.24 0.85 0.80 comp21-2idx 0.31 0.28 1.55 0.35 0.80 cod105 8.39 6.13 9.06 7.46 0.82 decomp2 0.08 0.10 0.19 0.12 0.83 cryptanalysiskb128n5obj16 10.05 8.70 29.53 10.27 0.85 fiball 0.15 0.12 0.71 0.14 0.86 blp-ar98 0.07 0.10 0.10 0.11 0.91 dano3_3 16.02 17.37 46.88 19.06 0.91 dano3_5 16.04 17.42 46.79 19.03 0.92 neos-3555904-turama 1.31 1.27 1.31 1.37 0.93 neos-3988577-wolgan 300.00 281.89 278.89 300.00 0.94 neos-1171737 0.17 0.19 0.70 0.20 0.95 drayage-25-23 0.05 0.04 0.08 0.04 1.00 h80x6320d 0.04 0.04 0.05 0.04 1.00 highschool1-aigio 300.00 300.00 300.00 300.00 1.00 icir97_tension 0.02 0.02 0.03 0.02 1.00 leo1 0.07 0.07 0.10 0.07 1.00 leo2 0.12 0.13 0.13 0.13 1.00 neos-1456979 0.05 0.04 0.05 0.04 1.00 neos8 0.26 0.26 0.38 0.26 1.00 nursesched-sprint02 0.23 0.23 0.38 0.23 1.00 physiciansched3-3 300.00 300.00 300.00 300.00 1.00 radiationm18-12-05 0.13 0.14 0.23 0.14 1.00 rail02 300.00 300.00 300.00 300.00 1.00 s100 300.00 300.00 300.00 300.00 1.00 savsched1 300.00 300.00 300.00 300.00 1.00 supportcase19 300.00 300.00 300.00 300.00 1.00 swath3 0.02 0.03 0.03 0.03 1.00 traininstance6 0.04 0.03 0.04 0.03 1.00 neos-873061 1.46 1.43 1.46 1.38 1.04 neos-957323 12.14 8.37 300.00 7.74 1.08 mzzv42z 0.91 1.03 10.10 0.95 1.08 neos-3402294-bobin 1.29 1.33 3.07 1.20 1.11 germanrr 0.30 0.30 0.31 0.27 1.11 s250r10 71.91 81.59 300.00 71.74 1.14 supportcase6 4.93 4.83 7.39 4.18 1.16 neos-1582420 0.08 0.07 0.12 0.06 1.17 supportcase40 0.29 0.28 0.24 0.24 1.17 neos-4763324-toguru 5.82 5.85 8.32 5.00 1.17 neos-1122047 1.87 1.90 2.09 1.61 1.18 sp98ar 0.34 0.37 0.39 0.31 1.19 neos-4413714-turia 2.10 2.33 3.97 1.93 1.21 rail507 4.37 3.52 7.17 2.84 1.24 drayage-100-23 0.04 0.05 0.07 0.04 1.25 neos-4738912-atrato 0.04 0.05 0.05 0.04 1.25 traininstance2 0.04 0.05 0.09 0.04 1.25 nexp-150-20-8-5 0.10 0.09 0.10 0.07 1.29 fast0507 3.95 3.58 7.36 2.77 1.29 sp97ar 0.44 0.43 0.40 0.33 1.30 irp 0.12 0.12 0.11 0.09 1.33 swath1 0.03 0.04 0.04 0.03 1.33 radiationm40-10-02 0.97 0.95 1.58 0.71 1.34 cmflsp50-24-8-8 0.58 0.59 0.77 0.44 1.34 map16715-04 9.87 9.21 13.26 6.80 1.35 comp07-2idx 1.26 1.51 4.03 1.10 1.37 map10 8.54 8.67 11.08 6.25 1.39 cbs-cta 0.14 0.14 0.42 0.10 1.40 neos-2978193-inde 0.09 0.07 0.16 0.05 1.40 trento1 3.50 3.10 3.08 2.21 1.40 hypothyroid-k1 4.42 4.40 4.36 3.01 1.46 uccase9 8.04 7.66 11.54 5.20 1.47 qap10 9.80 9.87 15.57 6.68 1.48 neos-827175 0.44 0.43 9.36 0.29 1.48 neos-2987310-joes 1.59 1.49 1.52 1.00 1.49 bnatt500 0.20 0.21 0.29 0.14 1.50 mushroom-best 0.28 0.30 0.26 0.20 1.50 neos-960392 3.29 3.99 8.93 2.66 1.50 air05 0.29 0.27 0.28 0.18 1.50 reblock115 0.14 0.14 0.16 0.09 1.56 neos-4532248-waihi 1.40 1.43 2.61 0.90 1.59 uct-subprob 0.12 0.13 0.11 0.08 1.62 ns1830653 0.25 0.23 0.42 0.14 1.64 fhnw-binpack4-48 0.09 0.10 0.07 0.06 1.67 neos-3083819-nubu 0.05 0.05 0.06 0.03 1.67 ran14x18-disj-8 0.05 0.05 0.04 0.03 1.67 rocI-4-11 0.09 0.10 0.11 0.06 1.67 roll3000 0.10 0.10 0.12 0.06 1.67 opm2-z10-s4 75.72 75.53 89.23 44.75 1.69 istanbul-no-cutoff 1.63 1.62 0.71 0.94 1.72 neos-3656078-kumeu 0.40 0.40 2.37 0.23 1.74 neos-662469 0.86 0.93 1.65 0.53 1.75 k1mushroom 28.33 29.61 31.00 16.80 1.76 rmatr200-p5 8.10 8.20 7.50 4.61 1.78 sing326 9.33 8.37 9.54 4.69 1.78 neos-933966 9.15 5.17 16.79 2.80 1.85 rmatr100-p10 0.33 0.26 0.27 0.14 1.86 bnatt400 0.13 0.15 0.16 0.08 1.88 mcsched 0.30 0.30 0.27 0.16 1.88 atlanta-ip 12.20 9.04 6.85 4.54 1.99 assign1-5-8 0.02 0.02 0.03 0.01 2.00 b1c1s1 0.06 0.04 0.05 0.02 2.00 bppc4-08 0.04 0.04 0.08 0.02 2.00 eil33-2 0.06 0.06 0.05 0.03 2.00 fhnw-binpack4-4 0.03 0.02 0.03 0.01 2.00 ic97_potential 0.02 0.02 0.02 0.01 2.00 mik-250-20-75-4 0.02 0.02 0.02 0.01 2.00 neos-3024952-loue 0.32 0.40 0.41 0.20 2.00 neos-4954672-berkel 0.02 0.02 0.02 0.01 2.00 pg5_34 0.03 0.02 0.03 0.01 2.00 tr12-30 0.02 0.02 0.03 0.01 2.00 graph20-20-1rand 0.30 0.25 0.23 0.12 2.08 neos-3216931-puriri 4.20 6.83 7.33 3.23 2.11 ns1208400 0.76 0.78 3.96 0.36 2.17 chromaticindex512-7 53.03 46.15 16.62 21.24 2.17 splice1k1 19.92 20.18 21.62 9.17 2.20 eilA101-2 3.29 3.08 2.68 1.39 2.22 nw04 1.71 1.36 0.44 0.61 2.23 sing44 12.54 12.80 9.62 5.69 2.25 gfd-schedulen180f7d50m30k18 15.81 15.35 80.00 6.81 2.25 n2seq36q 0.71 0.56 0.46 0.24 2.33 triptim1 132.50 121.77 71.42 51.43 2.37 netdiversion 22.67 22.84 7.64 9.43 2.42 piperout-08 0.41 0.39 0.39 0.16 2.44 chromaticindex1024-7 262.17 232.72 45.86 93.65 2.48 satellites2-60-fs 6.89 8.34 4.17 3.34 2.50 neos-4387871-tavua 0.08 0.10 0.10 0.04 2.50 neos-950242 0.46 0.45 1.05 0.18 2.50 nu25-pr12 0.04 0.05 0.05 0.02 2.50 rococoB10-011000 0.15 0.15 0.13 0.06 2.50 rococoC10-001000 0.06 0.05 0.04 0.02 2.50 glass-sc 0.33 0.31 0.31 0.12 2.58 seymour1 0.97 1.00 0.83 0.38 2.63 seymour 0.95 1.01 0.83 0.38 2.66 unitcal_7 1.05 1.04 0.96 0.39 2.67 sorrell3 1.14 1.10 1.07 0.40 2.75 bab2 75.19 80.63 300.00 28.97 2.78 bab6 34.02 40.19 143.43 14.06 2.86 neos-1445765 0.25 0.26 0.16 0.09 2.89 net12 0.96 1.03 0.55 0.35 2.94 50v-10 0.03 0.03 0.02 0.01 3.00 binkar10_1 0.03 0.03 0.02 0.01 3.00 cost266-UUE 0.06 0.06 0.03 0.02 3.00 gmu-35-40 0.03 0.03 0.03 0.01 3.00 gmu-35-50 0.04 0.03 0.05 0.01 3.00 lotsize 0.03 0.03 0.03 0.01 3.00 n5-3 0.05 0.06 0.03 0.02 3.00 neos-4338804-snowy 0.02 0.03 0.03 0.01 3.00 pg 0.02 0.03 0.03 0.01 3.00 csched007 0.16 0.17 0.19 0.05 3.40 peg-solitaire-a3 2.14 1.78 1.88 0.52 3.42 milo-v12-6-r2-40-1 0.18 0.18 0.25 0.05 3.60 uccase12 5.76 5.36 72.32 1.38 3.88 app1-1 0.10 0.08 0.08 0.02 4.00 csched008 0.13 0.12 0.11 0.03 4.00 neos-3627168-kasai 0.04 0.04 0.04 0.01 4.00 neos17 0.03 0.04 0.03 0.01 4.00 p200x1188c 0.04 0.04 0.03 0.01 4.00 rail01 300.00 300.00 223.84 71.23 4.21 CMS750_4 0.63 0.63 0.38 0.14 4.50 piperout-27 1.23 1.26 0.67 0.26 4.85 neos-631710 215.36 155.73 300.00 31.97 4.87 neos-4722843-widden 7.67 7.75 1.17 1.54 5.03 irish-electricity 109.17 300.00 181.58 59.53 5.04 ex10 300.00 300.00 300.00 59.25 5.06 fastxgemm-n2r6s0t2 0.40 0.47 0.18 0.08 5.87 neos-2075418-temuka 300.00 300.00 128.30 50.57 5.93 beasleyC3 0.06 0.06 0.05 0.01 6.00 snp-02-004-104 24.10 21.83 14.17 2.83 7.71 app1-2 5.77 5.58 5.03 0.70 7.97 mc11 0.08 0.08 0.06 0.01 8.00 brazil3 61.89 63.40 300.00 7.24 8.76 ex9 300.00 132.66 300.00 14.07 9.43 enlight_hard 0.02 0.01 0.02 0.00 10.00 gen-ip054 0.02 0.01 0.03 0.00 10.00 pk1 0.01 0.01 0.02 0.00 10.00 exp-1-500-5-5 0.02 0.02 0.02 0.00 20.00 gen-ip002 0.01 0.02 0.02 0.00 20.00 glass4 0.02 0.02 0.02 0.00 20.00 graphdraw-domain 0.02 0.02 0.02 0.00 20.00 mad 0.02 0.02 0.02 0.00 20.00 markshare2 0.01 0.02 0.02 0.00 20.00 markshare_4_0 0.01 0.02 0.02 0.00 20.00 mas74 0.01 0.02 0.03 0.00 20.00 mas76 0.02 0.02 0.02 0.00 20.00 neos-2657525-crna 0.03 0.02 0.03 0.00 20.00 neos-3046615-murg 0.02 0.02 0.02 0.00 20.00 neos-911970 0.03 0.02 0.03 0.00 20.00 neos5 0.02 0.02 0.02 0.00 20.00 neos859080 0.01 0.02 0.01 0.00 20.00 supportcase26 0.02 0.02 0.03 0.00 20.00 timtab1 0.02 0.02 0.01 0.00 20.00 neos-3754480-nidda 0.02 0.03 0.02 0.00 30.00 sp150x300d 0.02 0.03 0.02 0.00 30.00 Geomean v9/v10: 1.0157 Shifted(+1s): 1.0209 Geomean Baseline/v10: 1.4393 Shifted(+1s): 1.2823 Geomean v10/HiGHS: 1.5021 Shifted(+1s): 0.9925 (240 problems)
Signed-off-by: Christopher Maes <cmaes@nvidia.com>
This PR includes the following: