-
Notifications
You must be signed in to change notification settings - Fork 148
[DONOTMERGE] Refactor root relaxation #976
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/26.04
Are you sure you want to change the base?
Changes from all commits
d3b7b12
6aae07d
1b6d98a
e311548
c522274
55b2afb
1d213bd
9937868
0f1a6f9
e2ea687
d44661d
5309ca4
c9e39d3
c99e1c3
07cb595
953b83e
787fadf
197bf8d
990bcd0
40b9e49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| release/26.04 | ||
| main | ||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -331,6 +331,7 @@ void compute_dual_solution_from_basis(const lp_problem_t<i_t, f_t>& lp, | |
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's not include the dual push changes in this PR. This makes the PR difficult to review. |
||
| template <typename i_t, typename f_t> | ||
| i_t dual_push(const lp_problem_t<i_t, f_t>& lp, | ||
| const csr_matrix_t<i_t, f_t>& Arow, | ||
| const simplex_solver_settings_t<i_t, f_t>& settings, | ||
| f_t start_time, | ||
| lp_solution_t<i_t, f_t>& solution, | ||
|
|
@@ -387,6 +388,9 @@ i_t dual_push(const lp_problem_t<i_t, f_t>& lp, | |
| std::vector<f_t>& y = solution.y; | ||
| const std::vector<f_t>& x = solution.x; | ||
| i_t num_pushes = 0; | ||
| std::vector<f_t> delta_zN(n - m); | ||
| std::vector<f_t> delta_expanded; // workspace for sparse path (delta_y is sparse enough) | ||
| std::vector<f_t> delta_y_dense; // workspace for dense path (delta_y is not sparse enough) | ||
| while (superbasic_list.size() > 0) { | ||
| const i_t s = superbasic_list.back(); | ||
| const i_t basic_leaving_index = superbasic_list_index.back(); | ||
|
|
@@ -401,11 +405,9 @@ i_t dual_push(const lp_problem_t<i_t, f_t>& lp, | |
| es_sparse.x[0] = -delta_zs; | ||
|
|
||
| // B^T delta_y = -delta_zs*es | ||
| std::vector<f_t> delta_y(m); | ||
| sparse_vector_t<i_t, f_t> delta_y_sparse(m, 1); | ||
| sparse_vector_t<i_t, f_t> UTsol_sparse(m, 1); | ||
| ft.b_transpose_solve(es_sparse, delta_y_sparse, UTsol_sparse); | ||
| delta_y_sparse.scatter(delta_y); | ||
|
|
||
| // We solved B^T delta_y = -delta_zs*es, but for the update we need | ||
| // U^T*etilde = es. | ||
|
|
@@ -416,16 +418,38 @@ i_t dual_push(const lp_problem_t<i_t, f_t>& lp, | |
| } | ||
|
|
||
| // delta_zN = -N^T delta_y | ||
| std::vector<f_t> delta_zN(n - m); | ||
| 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]; | ||
| f_t dot = 0.0; | ||
| for (i_t p = col_start; p < col_end; ++p) { | ||
| dot += lp.A.x[p] * delta_y[lp.A.i[p]]; | ||
| // Choose sparse vs dense method by delta_y sparsity (match dual simplex: sparse if <= 30% nnz) | ||
| std::fill(delta_zN.begin(), delta_zN.end(), 0.); | ||
| const bool use_sparse = (delta_y_sparse.i.size() * 1.0 / m) <= 0.3; | ||
|
|
||
| if (use_sparse) { | ||
| delta_expanded.resize(n); | ||
| std::fill(delta_expanded.begin(), delta_expanded.end(), 0.); | ||
| for (i_t nnz_idx = 0; nnz_idx < static_cast<i_t>(delta_y_sparse.i.size()); ++nnz_idx) { | ||
| const i_t row = delta_y_sparse.i[nnz_idx]; | ||
| const f_t val = delta_y_sparse.x[nnz_idx]; | ||
| const i_t row_start = Arow.row_start[row]; | ||
| const i_t row_end = Arow.row_start[row + 1]; | ||
| for (i_t p = row_start; p < row_end; ++p) { | ||
| const i_t col = Arow.j[p]; | ||
| delta_expanded[col] += Arow.x[p] * val; | ||
| } | ||
| } | ||
| for (i_t k = 0; k < n - m; ++k) { | ||
| delta_zN[k] = -delta_expanded[nonbasic_list[k]]; | ||
| } | ||
| } else { | ||
| delta_y_sparse.to_dense(delta_y_dense); | ||
| for (i_t k = 0; k < n - m; ++k) { | ||
| const i_t j = nonbasic_list[k]; | ||
| f_t dot = 0.0; | ||
| const i_t c_start = lp.A.col_start[j]; | ||
| const i_t c_end = lp.A.col_start[j + 1]; | ||
| for (i_t p = c_start; p < c_end; ++p) { | ||
| dot += lp.A.x[p] * delta_y_dense[lp.A.i[p]]; | ||
| } | ||
| delta_zN[k] = -dot; | ||
| } | ||
| delta_zN[k] = -dot; | ||
| } | ||
|
|
||
| i_t entering_index = -1; | ||
|
|
@@ -435,8 +459,10 @@ i_t dual_push(const lp_problem_t<i_t, f_t>& lp, | |
| assert(step_length >= -1e-6); | ||
|
|
||
| // y <- y + step_length * delta_y | ||
| for (i_t i = 0; i < m; ++i) { | ||
| y[i] += step_length * delta_y[i]; | ||
| // Optimized: Only update non-zero elements from sparse representation | ||
| for (i_t nnz_idx = 0; nnz_idx < delta_y_sparse.i.size(); ++nnz_idx) { | ||
| const i_t i = delta_y_sparse.i[nnz_idx]; | ||
| y[i] += step_length * delta_y_sparse.x[nnz_idx]; | ||
| } | ||
|
|
||
| // z <- z + step_length * delta z | ||
|
|
@@ -725,7 +751,6 @@ i_t primal_push(const lp_problem_t<i_t, f_t>& lp, | |
| { | ||
| const i_t m = lp.num_rows; | ||
| const i_t n = lp.num_cols; | ||
|
|
||
| settings.log.debug("Primal push: superbasic %ld\n", superbasic_list.size()); | ||
|
|
||
| std::vector<f_t>& x = solution.x; | ||
|
|
@@ -1002,6 +1027,7 @@ i_t primal_push(const lp_problem_t<i_t, f_t>& lp, | |
| } | ||
| solution.x = x_compare; | ||
| solution.iterations += num_pushes; | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
|
|
@@ -1190,6 +1216,9 @@ crossover_status_t crossover(const lp_problem_t<i_t, f_t>& lp, | |
| f_t crossover_start = tic(); | ||
| f_t work_estimate = 0; | ||
|
|
||
| csr_matrix_t<i_t, f_t> Arow(m, n, 1); | ||
| lp.A.to_compressed_row(Arow); | ||
|
|
||
| settings.log.printf("\n"); | ||
| settings.log.printf("Starting crossover\n"); | ||
|
|
||
|
|
@@ -1331,8 +1360,16 @@ crossover_status_t crossover(const lp_problem_t<i_t, f_t>& lp, | |
| basis_update_mpf_t ft(L, U, p, settings.refactor_frequency); | ||
| verify_basis<i_t, f_t>(m, n, vstatus); | ||
| compare_vstatus_with_lists<i_t, f_t>(m, n, basic_list, nonbasic_list, vstatus); | ||
| i_t dual_push_status = dual_push( | ||
| lp, settings, start_time, solution, ft, basic_list, nonbasic_list, superbasic_list, vstatus); | ||
| i_t dual_push_status = dual_push(lp, | ||
| Arow, | ||
| settings, | ||
| start_time, | ||
| solution, | ||
| ft, | ||
| basic_list, | ||
| nonbasic_list, | ||
| superbasic_list, | ||
| vstatus); | ||
| if (dual_push_status < 0) { return return_to_status(dual_push_status); } | ||
| settings.log.debug("basic list size %ld m %d\n", basic_list.size(), m); | ||
| settings.log.debug("nonbasic list size %ld n - m %d\n", nonbasic_list.size(), n - m); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,8 @@ | |
|
|
||
| #include <dual_simplex/presolve.hpp> | ||
|
|
||
| #include <cuopt/linear_programming/constants.h> | ||
|
|
||
| #include <dual_simplex/bounds_strengthening.hpp> | ||
| #include <dual_simplex/folding.hpp> | ||
| #include <dual_simplex/right_looking_lu.hpp> | ||
|
|
@@ -1571,4 +1573,62 @@ template void uncrush_solution<int, double>(const presolve_info_t<int, double>& | |
|
|
||
| #endif | ||
|
|
||
| #if CUOPT_INSTANTIATE_FLOAT | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add FLOAT instation in another PR? I think there is a ton of work to get the code to compile and work with float. I'm not sure we should do that in the same PR that we are changing the concurrent root solve. |
||
|
|
||
| template void convert_user_problem<int, float>( | ||
| const user_problem_t<int, float>& user_problem, | ||
| const simplex_solver_settings_t<int, float>& settings, | ||
| lp_problem_t<int, float>& problem, | ||
| std::vector<int>& new_slacks, | ||
| dualize_info_t<int, float>& dualize_info); | ||
|
|
||
| template void convert_user_lp_with_guess<int, float>( | ||
| const user_problem_t<int, float>& user_problem, | ||
| const lp_solution_t<int, float>& initial_solution, | ||
| const std::vector<float>& initial_slack, | ||
| lp_problem_t<int, float>& lp, | ||
| lp_solution_t<int, float>& converted_solution); | ||
|
|
||
| template int presolve<int, float>(const lp_problem_t<int, float>& original, | ||
| const simplex_solver_settings_t<int, float>& settings, | ||
| lp_problem_t<int, float>& presolved, | ||
| presolve_info_t<int, float>& presolve_info); | ||
|
|
||
| template void crush_primal_solution<int, float>(const user_problem_t<int, float>& user_problem, | ||
| const lp_problem_t<int, float>& problem, | ||
| const std::vector<float>& user_solution, | ||
| const std::vector<int>& new_slacks, | ||
| std::vector<float>& solution); | ||
|
|
||
| template float crush_dual_solution<int, float>(const user_problem_t<int, float>& user_problem, | ||
| const lp_problem_t<int, float>& problem, | ||
| const std::vector<int>& new_slacks, | ||
| const std::vector<float>& user_y, | ||
| const std::vector<float>& user_z, | ||
| std::vector<float>& y, | ||
| std::vector<float>& z); | ||
|
|
||
| template void uncrush_primal_solution<int, float>(const user_problem_t<int, float>& user_problem, | ||
| const lp_problem_t<int, float>& problem, | ||
| const std::vector<float>& solution, | ||
| std::vector<float>& user_solution); | ||
|
|
||
| template void uncrush_dual_solution<int, float>(const user_problem_t<int, float>& user_problem, | ||
| const lp_problem_t<int, float>& problem, | ||
| const std::vector<float>& y, | ||
| const std::vector<float>& z, | ||
| std::vector<float>& user_y, | ||
| std::vector<float>& user_z); | ||
|
|
||
| template void uncrush_solution<int, float>(const presolve_info_t<int, float>& presolve_info, | ||
| const simplex_solver_settings_t<int, float>& settings, | ||
| const std::vector<float>& crushed_x, | ||
| const std::vector<float>& crushed_y, | ||
| const std::vector<float>& crushed_z, | ||
| std::vector<float>& uncrushed_x, | ||
| std::vector<float>& uncrushed_y, | ||
| std::vector<float>& uncrushed_z); | ||
|
|
||
| #endif | ||
|
Comment on lines
+1576
to
+1632
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find where float instantiations might be called and check tolerance handling
rg -n --type=cpp -C3 'presolve<int,\s*float>|convert_user_problem<int,\s*float>' cpp/src/Repository: NVIDIA/cuopt Length of output: 1224 🏁 Script executed: #!/bin/bash
# Find where these float instantiated functions are called
echo "=== Searching for calls to float-instantiated functions ==="
rg -n --type=cpp 'presolve<int,\s*float>|convert_user_problem<int,\s*float>|crush_primal_solution<int,\s*float>|crush_dual_solution<int,\s*float>' --no-filename cpp/src/ | grep -v "^template"
echo -e "\n=== Checking tolerance constants in presolve.cpp ==="
rg -n '1e-13|1e-6|1e-7|tolerance' cpp/src/dual_simplex/presolve.cpp | head -20Repository: NVIDIA/cuopt Length of output: 1231 🏁 Script executed: #!/bin/bash
# Check if there's any conditional logic for float precision handling
echo "=== Looking for float-specific handling or guards ==="
rg -n --type=cpp -B2 -A2 'CUOPT_INSTANTIATE_FLOAT|float.*tolerance|precision' cpp/src/dual_simplex/presolve.cpp | head -40
echo -e "\n=== Check if float instantiations are guarded or conditionally used ==="
rg -n --type=cpp 'if.*float|INSTANTIATE.*FLOAT' cpp/src/ | head -20Repository: NVIDIA/cuopt Length of output: 1400 🏁 Script executed: #!/bin/bash
# Find the actual usage of presolve in the codebase to understand calling patterns
echo "=== Finding where presolve function is called ==="
rg -n 'presolve\(' cpp/src/ --type=cpp | grep -v template | grep -v 'presolve\[' | head -20
echo -e "\n=== Check headers for documentation about float vs double ==="
find cpp/include -name "*.h" -o -name "*.hpp" | xargs rg -l 'float.*precision|tolerance' | head -5Repository: NVIDIA/cuopt Length of output: 1357 🏁 Script executed: #!/bin/bash
# Find where CUOPT_INSTANTIATE_FLOAT is defined
echo "=== Where is CUOPT_INSTANTIATE_FLOAT defined? ==="
rg -n 'define.*CUOPT_INSTANTIATE_FLOAT|CUOPT_INSTANTIATE_FLOAT' cpp/ | head -20
echo -e "\n=== Check build configuration ==="
find . -name "CMakeLists.txt" -o -name "*.cmake" | xargs rg -l 'CUOPT_INSTANTIATE_FLOAT' 2>/dev/nullRepository: NVIDIA/cuopt Length of output: 712 🏁 Script executed: #!/bin/bash
# Look at solve.cpp context where presolve is called
echo "=== Context around presolve calls in solve.cpp ==="
sed -n '140,170p' cpp/src/dual_simplex/solve.cpp
echo "---"
sed -n '340,360p' cpp/src/dual_simplex/solve.cppRepository: NVIDIA/cuopt Length of output: 2563 🏁 Script executed: #!/bin/bash
# Check the actual template definition of presolve to understand how it handles types
echo "=== Presolve template definition signature ==="
sed -n '819,830p' cpp/src/dual_simplex/presolve.cpp
echo -e "\n=== Check if there are any float-specific tolerance adjustments ==="
rg -n 'std::is_same.*float|if constexpr.*float|enable_if.*float' cpp/src/dual_simplex/presolve.cppRepository: NVIDIA/cuopt Length of output: 617 🏁 Script executed: #!/bin/bash
# Check tolerance usage more carefully - are they derived from precision?
echo "=== Check if tolerances are derived from numeric traits ==="
rg -n 'numeric_limits|epsilon|std::numeric' cpp/src/dual_simplex/presolve.cpp
echo -e "\n=== Look at the actual values and how they're used ==="
sed -n '440,460p' cpp/src/dual_simplex/presolve.cpp
echo "---"
sed -n '490,505p' cpp/src/dual_simplex/presolve.cppRepository: NVIDIA/cuopt Length of output: 1396 Float template instantiations use hardcoded tolerances unsuitable for single-precision arithmetic. The float-instantiated functions (lines 1576–1632) use tolerances hardcoded for double precision: More critically, Either:
🤖 Prompt for AI Agents |
||
|
|
||
| } // namespace cuopt::linear_programming::dual_simplex | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid pointing release flow to
mainfor this branch marker.Using
mainhere can pull moving-head workflow/config changes into a release line and make builds non-reproducible. Prefer pinning to the release branch/tag used for this train (e.g.,release/26.04or an explicit immutable ref).🤖 Prompt for AI Agents