From 0bc415cb8bb9f21a2783d70c9a1107b34994e20a Mon Sep 17 00:00:00 2001 From: yuwenchen95 Date: Fri, 14 Aug 2026 05:47:45 -0700 Subject: [PATCH 1/4] Make cudss parameters visible Signed-off-by: yuwenchen95 --- .../mathematical_optimization/constants.h | 4 ++ .../pdlp/solver_settings.hpp | 4 ++ cpp/src/barrier/sparse_cholesky.cuh | 37 +++++++++++++++++ .../dual_simplex/simplex_solver_settings.hpp | 12 +++++- cpp/src/grpc/codegen/field_registry.yaml | 14 +++++++ .../codegen/generated/cuopt_remote_data.proto | 4 ++ .../generated_pdlp_settings_to_proto.inc | 4 ++ .../generated_proto_to_pdlp_settings.inc | 8 ++++ cpp/src/math_optimization/solver_settings.cu | 4 ++ cpp/src/pdlp/solve.cu | 4 ++ .../grpc/grpc_client_test.cpp | 10 +++++ docs/cuopt/source/convex-settings.rst | 40 ++++++++++++++++++ .../cuopt_server/tests/test_lp.py | 41 +++++++++++++++---- .../linear_programming/data_definition.py | 22 ++++++++++ 14 files changed, 199 insertions(+), 9 deletions(-) diff --git a/cpp/include/cuopt/mathematical_optimization/constants.h b/cpp/include/cuopt/mathematical_optimization/constants.h index 04ac3355ab..4a3885e51f 100644 --- a/cpp/include/cuopt/mathematical_optimization/constants.h +++ b/cpp/include/cuopt/mathematical_optimization/constants.h @@ -52,6 +52,10 @@ #define CUOPT_BARRIER_STEP_SCALE "barrier_step_scale" #define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns" #define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic" +#define CUOPT_CUDSS_ND_NLEVELS "cudss_nd_nlevels" +#define CUOPT_CUDSS_HYBRID_MODE "cudss_hybrid_mode" +#define CUOPT_CUDSS_HYBRID_EXECUTE_MODE "cudss_hybrid_execute_mode" +#define CUOPT_CUDSS_HOST_NTHREADS "cudss_host_nthreads" #define CUOPT_PRESOLVE "presolve" #define CUOPT_MIP_PROBING "mip_probing" #define CUOPT_DUAL_POSTSOLVE "dual_postsolve" diff --git a/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp b/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp index ffcf3fad7a..180b3d9e83 100644 --- a/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp +++ b/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp @@ -290,6 +290,10 @@ class pdlp_solver_settings_t { bool per_constraint_residual{false}; bool crossover{false}; bool cudss_deterministic{false}; + i_t cudss_nd_nlevels{-1}; + bool cudss_hybrid_mode{false}; + bool cudss_hybrid_execute_mode{false}; + i_t cudss_host_nthreads{-1}; i_t folding{-1}; i_t augmented{-1}; i_t dualize{-1}; diff --git a/cpp/src/barrier/sparse_cholesky.cuh b/cpp/src/barrier/sparse_cholesky.cuh index 3d88fef2ce..4121b58715 100644 --- a/cpp/src/barrier/sparse_cholesky.cuh +++ b/cpp/src/barrier/sparse_cholesky.cuh @@ -296,6 +296,43 @@ class sparse_cholesky_cudss_t : public sparse_cholesky_base_t { status, "cudssConfigSet for deterministic mode"); } + + if (settings_.cudss_nd_nlevels >= 0) { + settings_.log.printf("cuDSS ND levels : %d\n", settings_.cudss_nd_nlevels); + int32_t nd_nlevels = settings_.cudss_nd_nlevels; + CUDSS_CALL_AND_CHECK_EXIT( + cudssConfigSet(solverConfig, CUDSS_CONFIG_ND_NLEVELS, &nd_nlevels, sizeof(int32_t)), + status, + "cudssConfigSet for nd nlevels"); + } + + if (settings_.cudss_hybrid_mode) { + settings_.log.printf("cuDSS hybrid mode : enabled\n"); + int32_t hybrid_mode = 1; + CUDSS_CALL_AND_CHECK_EXIT( + cudssConfigSet(solverConfig, CUDSS_CONFIG_HYBRID_MODE, &hybrid_mode, sizeof(int32_t)), + status, + "cudssConfigSet for hybrid mode"); + } + + if (settings_.cudss_hybrid_execute_mode) { + settings_.log.printf("cuDSS hybrid execute mode : enabled\n"); + int32_t hybrid_execute_mode = 1; + CUDSS_CALL_AND_CHECK_EXIT( + cudssConfigSet( + solverConfig, CUDSS_CONFIG_HYBRID_EXECUTE_MODE, &hybrid_execute_mode, sizeof(int32_t)), + status, + "cudssConfigSet for hybrid execute mode"); + } + + if (settings_.cudss_host_nthreads >= 0) { + settings_.log.printf("cuDSS host nthreads : %d\n", settings_.cudss_host_nthreads); + int32_t host_nthreads = settings_.cudss_host_nthreads; + CUDSS_CALL_AND_CHECK_EXIT( + cudssConfigSet(solverConfig, CUDSS_CONFIG_HOST_NTHREADS, &host_nthreads, sizeof(int32_t)), + status, + "cudssConfigSet for host nthreads"); + } #endif #if USE_ITERATIVE_REFINEMENT diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 9ec57505f9..0ba7e121f9 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -66,6 +66,10 @@ struct simplex_solver_settings_t { print_presolve_stats(true), barrier_presolve(false), cudss_deterministic(false), + cudss_nd_nlevels(-1), + cudss_hybrid_mode(false), + cudss_hybrid_execute_mode(false), + cudss_host_nthreads(-1), deterministic(false), barrier(false), eliminate_dense_columns(true), @@ -160,7 +164,13 @@ struct simplex_solver_settings_t { bool print_presolve_stats; // true to print presolve stats bool barrier_presolve; // true to use barrier presolve bool cudss_deterministic; // true to use cuDSS deterministic mode, false for non-deterministic - bool barrier; // true to use barrier method, false to use dual simplex method + i_t cudss_nd_nlevels; // -1 automatic/unset, else METIS nested-dissection depth for cuDSS + bool cudss_hybrid_mode; // true for hybrid CPU/GPU factor storage in cuDSS + bool cudss_hybrid_execute_mode; // true to overlap CPU/GPU work in cuDSS (only meaningful when + // cudss_hybrid_mode is true) + i_t cudss_host_nthreads; // -1 automatic/unset, else host worker thread count for cuDSS + // (only takes effect when a cuDSS threading layer is loaded) + bool barrier; // true to use barrier method, false to use dual simplex method bool deterministic; // true to use B&B deterministic mode, false to use non-deterministic mode bool eliminate_dense_columns; // true to eliminate dense columns from A*D*A^T bool barrier_iterative_refinement; // true to use iterative refinement for barrier method diff --git a/cpp/src/grpc/codegen/field_registry.yaml b/cpp/src/grpc/codegen/field_registry.yaml index 2fb7896027..e85062ce66 100644 --- a/cpp/src/grpc/codegen/field_registry.yaml +++ b/cpp/src/grpc/codegen/field_registry.yaml @@ -559,6 +559,20 @@ pdlp_settings: field_num: 33 type: int32 optional: true + - cudss_nd_nlevels: + field_num: 34 + type: int32 + optional: true + - cudss_hybrid_mode: + field_num: 35 + type: bool + - cudss_hybrid_execute_mode: + field_num: 36 + type: bool + - cudss_host_nthreads: + field_num: 37 + type: int32 + optional: true - save_best_primal_so_far: field_num: 28 type: bool diff --git a/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto b/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto index 4b1e36d134..4375f73792 100644 --- a/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto +++ b/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto @@ -194,6 +194,10 @@ message PDLPSolverSettings { optional bool barrier_iterative_refinement = 31; optional double barrier_step_scale = 32; optional int32 postsolve_info = 33; + optional int32 cudss_nd_nlevels = 34; + bool cudss_hybrid_mode = 35; + bool cudss_hybrid_execute_mode = 36; + optional int32 cudss_host_nthreads = 37; PDLPWarmStartData warm_start_data = 50; } diff --git a/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc b/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc index eb88b192d9..c431f5a2a6 100644 --- a/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc +++ b/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc @@ -36,6 +36,10 @@ pb_settings->set_barrier_iterative_refinement(settings.barrier_iterative_refinement); pb_settings->set_barrier_step_scale(settings.barrier_step_scale); pb_settings->set_postsolve_info(settings.postsolve_info); + pb_settings->set_cudss_nd_nlevels(settings.cudss_nd_nlevels); + pb_settings->set_cudss_hybrid_mode(settings.cudss_hybrid_mode); + pb_settings->set_cudss_hybrid_execute_mode(settings.cudss_hybrid_execute_mode); + pb_settings->set_cudss_host_nthreads(settings.cudss_host_nthreads); pb_settings->set_save_best_primal_so_far(settings.save_best_primal_so_far); pb_settings->set_first_primal_feasible(settings.first_primal_feasible); pb_settings->set_pdlp_precision(static_cast(settings.pdlp_precision)); diff --git a/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc b/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc index 29e6c4cca2..8ae03d9c4f 100644 --- a/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc +++ b/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc @@ -82,6 +82,14 @@ if (pb_settings.has_postsolve_info()) { settings.postsolve_info = pb_settings.postsolve_info(); } + if (pb_settings.has_cudss_nd_nlevels()) { + settings.cudss_nd_nlevels = pb_settings.cudss_nd_nlevels(); + } + settings.cudss_hybrid_mode = pb_settings.cudss_hybrid_mode(); + settings.cudss_hybrid_execute_mode = pb_settings.cudss_hybrid_execute_mode(); + if (pb_settings.has_cudss_host_nthreads()) { + settings.cudss_host_nthreads = pb_settings.cudss_host_nthreads(); + } settings.save_best_primal_so_far = pb_settings.save_best_primal_so_far(); settings.first_primal_feasible = pb_settings.first_primal_feasible(); if (pb_settings.has_pdlp_precision()) { diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index ba93c6c275..52881fe805 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -136,6 +136,8 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_FOLDING, &pdlp_settings.folding, -1, 1, -1}, {CUOPT_DUALIZE, &pdlp_settings.dualize, -1, 1, -1}, {CUOPT_ORDERING, &pdlp_settings.ordering, -1, 1, -1}, + {CUOPT_CUDSS_ND_NLEVELS, &pdlp_settings.cudss_nd_nlevels, -1, std::numeric_limits::max(), -1}, + {CUOPT_CUDSS_HOST_NTHREADS, &pdlp_settings.cudss_host_nthreads, -1, std::numeric_limits::max(), -1}, {CUOPT_BARRIER_DUAL_INITIAL_POINT, &pdlp_settings.barrier_dual_initial_point, -1, 1, -1}, {CUOPT_POSTSOLVE_INFO, &pdlp_settings.postsolve_info, -1, 1, -1}, {CUOPT_MIP_CUT_PASSES, &mip_settings.max_cut_passes, -1, std::numeric_limits::max(), 10}, @@ -204,6 +206,8 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_CROSSOVER, &pdlp_settings.crossover, false}, {CUOPT_ELIMINATE_DENSE_COLUMNS, &pdlp_settings.eliminate_dense_columns, true}, {CUOPT_CUDSS_DETERMINISTIC, &pdlp_settings.cudss_deterministic, false}, + {CUOPT_CUDSS_HYBRID_MODE, &pdlp_settings.cudss_hybrid_mode, false}, + {CUOPT_CUDSS_HYBRID_EXECUTE_MODE, &pdlp_settings.cudss_hybrid_execute_mode, false}, {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true}, {CUOPT_BARRIER_ITERATIVE_REFINEMENT, &pdlp_settings.barrier_iterative_refinement, true}, {CUOPT_MIP_PROBING, &mip_settings.probing, true}, diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 9e54bb1a11..b19b230d6b 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -517,6 +517,10 @@ std::tuple, simplex::lp_status_t, f_t, f_t, f_t barrier_settings.barrier_step_scale = settings.barrier_step_scale; barrier_settings.qcqp_ruiz_equilibration = settings.qcqp_ruiz_equilibration; barrier_settings.cudss_deterministic = settings.cudss_deterministic; + barrier_settings.cudss_nd_nlevels = settings.cudss_nd_nlevels; + barrier_settings.cudss_hybrid_mode = settings.cudss_hybrid_mode; + barrier_settings.cudss_hybrid_execute_mode = settings.cudss_hybrid_execute_mode; + barrier_settings.cudss_host_nthreads = settings.cudss_host_nthreads; barrier_settings.barrier_relaxed_feasibility_tol = settings.tolerances.relative_primal_tolerance; barrier_settings.barrier_relaxed_optimality_tol = settings.tolerances.relative_dual_tolerance; barrier_settings.barrier_relaxed_complementarity_tol = settings.tolerances.relative_gap_tolerance; diff --git a/cpp/tests/linear_programming/grpc/grpc_client_test.cpp b/cpp/tests/linear_programming/grpc/grpc_client_test.cpp index f8fed6eee3..7970863cd8 100644 --- a/cpp/tests/linear_programming/grpc/grpc_client_test.cpp +++ b/cpp/tests/linear_programming/grpc/grpc_client_test.cpp @@ -2237,6 +2237,10 @@ TEST(MapperRoundtrip, PDLPSettingsAllFields) orig.num_gpus = 4; orig.per_constraint_residual = true; orig.cudss_deterministic = true; + orig.cudss_nd_nlevels = 8; + orig.cudss_hybrid_mode = true; + orig.cudss_hybrid_execute_mode = true; + orig.cudss_host_nthreads = 4; orig.folding = 1; orig.augmented = 1; orig.dualize = 1; @@ -2278,6 +2282,10 @@ TEST(MapperRoundtrip, PDLPSettingsAllFields) EXPECT_EQ(restored.num_gpus, 4); EXPECT_EQ(restored.per_constraint_residual, true); EXPECT_EQ(restored.cudss_deterministic, true); + EXPECT_EQ(restored.cudss_nd_nlevels, 8); + EXPECT_EQ(restored.cudss_hybrid_mode, true); + EXPECT_EQ(restored.cudss_hybrid_execute_mode, true); + EXPECT_EQ(restored.cudss_host_nthreads, 4); EXPECT_EQ(restored.folding, 1); EXPECT_EQ(restored.augmented, 1); EXPECT_EQ(restored.dualize, 1); @@ -2434,6 +2442,8 @@ TEST(MapperRoundtrip, PDLPSettingsDefaultProtoPreservesAllCppDefaults) EXPECT_EQ(after.augmented, fresh.augmented); EXPECT_EQ(after.dualize, fresh.dualize); EXPECT_EQ(after.ordering, fresh.ordering); + EXPECT_EQ(after.cudss_nd_nlevels, fresh.cudss_nd_nlevels); + EXPECT_EQ(after.cudss_host_nthreads, fresh.cudss_host_nthreads); EXPECT_EQ(after.barrier_dual_initial_point, fresh.barrier_dual_initial_point); EXPECT_DOUBLE_EQ(after.barrier_step_scale, fresh.barrier_step_scale); // Enum-int32 fields (post-decode clamping defends out-of-range; default `0` diff --git a/docs/cuopt/source/convex-settings.rst b/docs/cuopt/source/convex-settings.rst index 7bff025e5d..afc3c9c062 100644 --- a/docs/cuopt/source/convex-settings.rst +++ b/docs/cuopt/source/convex-settings.rst @@ -284,6 +284,46 @@ cuDSS Deterministic Mode .. note:: The default value is ``false``. Enable deterministic mode if reproducibility is more important than performance. +cuDSS Nested-Dissection Levels +""""""""""""""""""""""""""""""" + +``CUOPT_CUDSS_ND_NLEVELS`` controls the METIS nested-dissection depth used by cuDSS during reordering. + +* ``-1``: Leave unset, cuDSS chooses (default) +* Non-negative value: Explicit nested-dissection depth + +.. note:: The default value is ``-1`` (unset). + +cuDSS Hybrid Mode +""""""""""""""""" + +``CUOPT_CUDSS_HYBRID_MODE`` controls whether cuDSS stores factorization data in host (CPU) memory in addition to GPU memory, instead of GPU-only storage. This is primarily useful when a problem's factors do not fit in GPU memory; for problems that already fit, GPU-only storage is typically faster. + +* ``true``: Use hybrid CPU/GPU factor storage +* ``false``: Use GPU-only factor storage (default) + +.. note:: The default value is ``false``. + +cuDSS Hybrid Execute Mode +"""""""""""""""""""""""""" + +``CUOPT_CUDSS_HYBRID_EXECUTE_MODE`` controls whether cuDSS overlaps CPU and GPU computation while in hybrid mode. This setting only has an effect when ``CUOPT_CUDSS_HYBRID_MODE`` is enabled. + +* ``true``: Overlap CPU/GPU work +* ``false``: Do not overlap CPU/GPU work (default) + +.. note:: The default value is ``false``. + +cuDSS Host Threads +""""""""""""""""""" + +``CUOPT_CUDSS_HOST_NTHREADS`` controls the number of host worker threads cuDSS uses. This only takes effect when a cuDSS threading layer is loaded (via the ``CUDSS_THREADING_LIB`` environment variable or the build-time threading layer default). + +* ``-1``: Leave unset, cuDSS chooses (default) +* Non-negative value: Explicit host thread count + +.. note:: The default value is ``-1`` (unset). + Dual Initial Point """""""""""""""""" diff --git a/python/cuopt_server/cuopt_server/tests/test_lp.py b/python/cuopt_server/cuopt_server/tests/test_lp.py index e3a683f8de..cafc44fb1b 100644 --- a/python/cuopt_server/cuopt_server/tests/test_lp.py +++ b/python/cuopt_server/cuopt_server/tests/test_lp.py @@ -142,22 +142,23 @@ def test_sample_milp( ) @pytest.mark.parametrize( "folding, dualize, ordering, augmented, eliminate_dense, cudss_determ, " - "dual_initial_point", + "dual_initial_point, cudss_nd_nlevels, cudss_hybrid_mode, " + "cudss_hybrid_execute_mode, cudss_host_nthreads", [ # Test automatic settings (default) - (-1, -1, -1, -1, True, False, -1), + (-1, -1, -1, -1, True, False, -1, -1, False, False, -1), # Test folding off, no dualization, cuDSS default ordering, ADAT system - (0, 0, 0, 0, True, False, 0), + (0, 0, 0, 0, True, False, 0, -1, False, False, -1), # Test folding on, force dualization, AMD ordering, augmented system - (1, 1, 1, 1, True, True, 1), + (1, 1, 1, 1, True, True, 1, 8, True, True, 4), # Test mixed settings: automatic folding, no dualize, AMD, augmented - (-1, 0, 1, 1, False, False, 0), + (-1, 0, 1, 1, False, False, 0, 4, True, False, 2), # Test no folding, automatic dualize, cuDSS default, ADAT - (0, -1, 0, 0, True, True, -1), + (0, -1, 0, 0, True, True, -1, -1, False, False, -1), # Test dual initial point with Lustig-Marsten-Shanno - (-1, -1, -1, -1, True, False, 0), + (-1, -1, -1, -1, True, False, 0, -1, False, False, -1), # Test dual initial point with least squares - (-1, -1, -1, 1, True, False, 1), + (-1, -1, -1, 1, True, False, 1, -1, False, False, -1), ], ) def test_barrier_solver_options( @@ -169,6 +170,10 @@ def test_barrier_solver_options( eliminate_dense, cudss_determ, dual_initial_point, + cudss_nd_nlevels, + cudss_hybrid_mode, + cudss_hybrid_execute_mode, + cudss_host_nthreads, ): """ Test the barrier solver (method=3) with various configuration options: @@ -181,6 +186,14 @@ def test_barrier_solver_options( nondeterministic - barrier_dual_initial_point: (-1) automatic, (0) Lustig-Marsten-Shanno, (1) dual least squares + - cudss_nd_nlevels: (-1) unset/automatic, else METIS nested-dissection + depth + - cudss_hybrid_mode: True for hybrid CPU/GPU factor storage, False for + GPU-only + - cudss_hybrid_execute_mode: True to overlap CPU/GPU work in hybrid mode, + False to not overlap + - cudss_host_nthreads: (-1) unset/automatic, else host worker thread + count for cuDSS """ data = get_std_data_for_lp() @@ -195,6 +208,12 @@ def test_barrier_solver_options( data["solver_config"]["eliminate_dense_columns"] = eliminate_dense data["solver_config"]["cudss_deterministic"] = cudss_determ data["solver_config"]["barrier_dual_initial_point"] = dual_initial_point + data["solver_config"]["cudss_nd_nlevels"] = cudss_nd_nlevels + data["solver_config"]["cudss_hybrid_mode"] = cudss_hybrid_mode + data["solver_config"]["cudss_hybrid_execute_mode"] = ( + cudss_hybrid_execute_mode + ) + data["solver_config"]["cudss_host_nthreads"] = cudss_host_nthreads res = get_lp(client, data) @@ -205,6 +224,12 @@ def test_barrier_solver_options( print(f"augmented={augmented}, eliminate_dense={eliminate_dense}") print(f"cudss_deterministic={cudss_determ}") print(f"barrier_dual_initial_point={dual_initial_point}") + print( + f"cudss_nd_nlevels={cudss_nd_nlevels}, " + f"cudss_hybrid_mode={cudss_hybrid_mode}, " + f"cudss_hybrid_execute_mode={cudss_hybrid_execute_mode}, " + f"cudss_host_nthreads={cudss_host_nthreads}" + ) print(res.json()) validate_lp_result( diff --git a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py index 6cd8f7828a..924d5b7339 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py @@ -514,6 +514,28 @@ class SolverConfig(BaseModel): description="Set if cuDSS should use deterministic mode. " "True to use deterministic mode, False to not use deterministic mode", ) + cudss_nd_nlevels: Optional[int] = Field( + default=-1, + description="Set the METIS nested-dissection depth used by cuDSS. " + "-1 to leave it unset (cuDSS default), or a non-negative depth", + ) + cudss_hybrid_mode: Optional[bool] = Field( + default=False, + description="Set if cuDSS should use hybrid CPU/GPU factor storage. " + "True to enable hybrid mode, False to keep factors GPU-only", + ) + cudss_hybrid_execute_mode: Optional[bool] = Field( + default=False, + description="Set if cuDSS should overlap CPU/GPU work in hybrid mode. " + "Only meaningful when cudss_hybrid_mode is True. " + "True to overlap CPU/GPU work, False to not overlap", + ) + cudss_host_nthreads: Optional[int] = Field( + default=-1, + description="Set the number of host worker threads cuDSS uses. " + "-1 to leave it unset (cuDSS default), or a non-negative thread " + "count. Only takes effect when a cuDSS threading layer is loaded", + ) crossover: Optional[bool] = Field( default=False, description="Set True to use crossover, False to not use crossover.", From 7eefdf2ac5be5f708d9c77bde4425e34a64511e7 Mon Sep 17 00:00:00 2001 From: yuwenchen95 Date: Fri, 14 Aug 2026 06:45:00 -0700 Subject: [PATCH 2/4] Make iterative refinement methods flexible Signed-off-by: yuwenchen95 --- .../mathematical_optimization/constants.h | 1 + .../pdlp/solver_settings.hpp | 1 + cpp/src/barrier/barrier.cu | 17 ++++++---- cpp/src/barrier/iterative_refinement.hpp | 20 +++++++----- .../dual_simplex/simplex_solver_settings.hpp | 10 +++--- cpp/src/grpc/codegen/field_registry.yaml | 4 +++ .../codegen/generated/cuopt_remote_data.proto | 1 + .../generated_pdlp_settings_to_proto.inc | 1 + .../generated_proto_to_pdlp_settings.inc | 3 ++ cpp/src/math_optimization/solver_settings.cu | 1 + cpp/src/pdlp/solve.cu | 32 ++++++++++--------- .../grpc/grpc_client_test.cpp | 13 +++++--- docs/cuopt/source/convex-settings.rst | 10 ++++++ .../cuopt_server/tests/test_lp.py | 26 +++++++++------ .../linear_programming/data_definition.py | 6 ++++ 15 files changed, 99 insertions(+), 47 deletions(-) diff --git a/cpp/include/cuopt/mathematical_optimization/constants.h b/cpp/include/cuopt/mathematical_optimization/constants.h index 4a3885e51f..2a79eece9b 100644 --- a/cpp/include/cuopt/mathematical_optimization/constants.h +++ b/cpp/include/cuopt/mathematical_optimization/constants.h @@ -49,6 +49,7 @@ #define CUOPT_BARRIER_DUAL_INITIAL_POINT "barrier_dual_initial_point" #define CUOPT_POSTSOLVE_INFO "postsolve_info" #define CUOPT_BARRIER_ITERATIVE_REFINEMENT "barrier_iterative_refinement" +#define CUOPT_BARRIER_ITERATIVE_REFINEMENT_METHOD "barrier_iterative_refinement_method" #define CUOPT_BARRIER_STEP_SCALE "barrier_step_scale" #define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns" #define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic" diff --git a/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp b/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp index 180b3d9e83..a206111c88 100644 --- a/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp +++ b/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp @@ -307,6 +307,7 @@ class pdlp_solver_settings_t { bool eliminate_dense_columns{true}; pdlp_precision_t pdlp_precision{pdlp_precision_t::DefaultPrecision}; bool barrier_iterative_refinement{true}; + i_t barrier_iterative_refinement_method{1}; // 0: fixed_point, 1: gmres (default) i_t barrier_soc_threshold{100}; f_t barrier_step_scale{0.9}; bool save_best_primal_so_far{false}; diff --git a/cpp/src/barrier/barrier.cu b/cpp/src/barrier/barrier.cu index edc7a00d1d..0b57994b4d 100644 --- a/cpp/src/barrier/barrier.cu +++ b/cpp/src/barrier/barrier.cu @@ -2279,7 +2279,8 @@ int barrier_solver_t::initial_point(iteration_data_t& data) if (settings.barrier_iterative_refinement) { const f_t ir_tol = data.has_sparse_cones() ? f_t(1e-12) : f_t(1e-8); - iterative_refinement(op, rhs, soln, ir_tol); + iterative_refinement( + op, rhs, soln, ir_tol, settings.barrier_iterative_refinement_method); } for (i_t k = 0; k < lp.num_cols; k++) { @@ -2904,9 +2905,13 @@ i_t barrier_solver_t::gpu_compute_search_direction(iteration_data_t( - op, data.d_augmented_rhs_, data.d_augmented_soln_, ir_tol); + const f_t ir_tol = data.has_sparse_cones() ? f_t(1e-12) : f_t(1e-8); + const f_t solve_err = + iterative_refinement(op, + data.d_augmented_rhs_, + data.d_augmented_soln_, + ir_tol, + settings.barrier_iterative_refinement_method); if (solve_err > 1e-1) { settings.log.printf("|| Aug (dx, dy) - aug_rhs || %e after IR\n", solve_err); } @@ -2992,8 +2997,8 @@ i_t barrier_solver_t::gpu_compute_search_direction(iteration_data_t(adat_op, data.d_h_, data.d_dy_); + const f_t adat_solve_err = iterative_refinement( + adat_op, data.d_h_, data.d_dy_, f_t(1e-8), settings.barrier_iterative_refinement_method); if (adat_solve_err > 1e-1) { settings.log.printf("||ADAT*dy - h|| %e after IR\n", adat_solve_err); } diff --git a/cpp/src/barrier/iterative_refinement.hpp b/cpp/src/barrier/iterative_refinement.hpp index e9a6152f06..92fd7e57d0 100644 --- a/cpp/src/barrier/iterative_refinement.hpp +++ b/cpp/src/barrier/iterative_refinement.hpp @@ -55,10 +55,10 @@ struct subtract_scaled_op { }; template -f_t iterative_refinement_simple(T& op, - const rmm::device_uvector& b, - rmm::device_uvector& x, - f_t tol = 1e-8) +f_t iterative_refinement_fixed_point(T& op, + const rmm::device_uvector& b, + rmm::device_uvector& x, + f_t tol = 1e-8) { rmm::device_uvector x_sav(x, x.stream()); @@ -365,13 +365,15 @@ template f_t iterative_refinement(T& op, const dense_vector_t& b, dense_vector_t& x, - f_t tol = 1e-8) + f_t tol = 1e-8, + i_t method = 1) { rmm::device_uvector d_b(b.size(), op.data_.handle_ptr->get_stream()); raft::copy(d_b.data(), b.data(), b.size(), op.data_.handle_ptr->get_stream()); rmm::device_uvector d_x(x.size(), op.data_.handle_ptr->get_stream()); raft::copy(d_x.data(), x.data(), x.size(), op.data_.handle_ptr->get_stream()); - auto err = iterative_refinement_gmres(op, d_b, d_x, tol); + auto err = (method == 0) ? iterative_refinement_fixed_point(op, d_b, d_x, tol) + : iterative_refinement_gmres(op, d_b, d_x, tol); raft::copy(x.data(), d_x.data(), x.size(), op.data_.handle_ptr->get_stream()); @@ -383,9 +385,11 @@ template f_t iterative_refinement(T& op, const rmm::device_uvector& b, rmm::device_uvector& x, - f_t tol = 1e-8) + f_t tol = 1e-8, + i_t method = 1) { - return iterative_refinement_gmres(op, b, x, tol); + return (method == 0) ? iterative_refinement_fixed_point(op, b, x, tol) + : iterative_refinement_gmres(op, b, x, tol); } } // namespace cuopt::mathematical_optimization::barrier diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 0ba7e121f9..d52dfd031d 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -74,6 +74,7 @@ struct simplex_solver_settings_t { barrier(false), eliminate_dense_columns(true), barrier_iterative_refinement(true), + barrier_iterative_refinement_method(1), barrier_step_scale(0.9), barrier_soc_threshold(100), num_gpus(1), @@ -172,10 +173,11 @@ struct simplex_solver_settings_t { // (only takes effect when a cuDSS threading layer is loaded) bool barrier; // true to use barrier method, false to use dual simplex method bool deterministic; // true to use B&B deterministic mode, false to use non-deterministic mode - bool eliminate_dense_columns; // true to eliminate dense columns from A*D*A^T - bool barrier_iterative_refinement; // true to use iterative refinement for barrier method - f_t barrier_step_scale; // step scale for barrier method - i_t barrier_soc_threshold; // SOC dimension above which rank-2 sparse scaling is used + bool eliminate_dense_columns; // true to eliminate dense columns from A*D*A^T + bool barrier_iterative_refinement; // true to use iterative refinement for barrier method + i_t barrier_iterative_refinement_method; // 0: fixed_point, 1: gmres + f_t barrier_step_scale; // step scale for barrier method + i_t barrier_soc_threshold; // SOC dimension above which rank-2 sparse scaling is used int num_gpus; // Number of GPUs to use (maximum of 2 gpus are supported at the moment) i_t folding; // -1 automatic, 0 don't fold, 1 fold i_t augmented; // -1 automatic, 0 to solve with ADAT, 1 to solve with augmented system diff --git a/cpp/src/grpc/codegen/field_registry.yaml b/cpp/src/grpc/codegen/field_registry.yaml index e85062ce66..740b4de4a2 100644 --- a/cpp/src/grpc/codegen/field_registry.yaml +++ b/cpp/src/grpc/codegen/field_registry.yaml @@ -573,6 +573,10 @@ pdlp_settings: field_num: 37 type: int32 optional: true + - barrier_iterative_refinement_method: + field_num: 38 + type: int32 + optional: true - save_best_primal_so_far: field_num: 28 type: bool diff --git a/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto b/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto index 4375f73792..58ad934db6 100644 --- a/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto +++ b/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto @@ -198,6 +198,7 @@ message PDLPSolverSettings { bool cudss_hybrid_mode = 35; bool cudss_hybrid_execute_mode = 36; optional int32 cudss_host_nthreads = 37; + optional int32 barrier_iterative_refinement_method = 38; PDLPWarmStartData warm_start_data = 50; } diff --git a/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc b/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc index c431f5a2a6..9f04dacb0e 100644 --- a/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc +++ b/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc @@ -40,6 +40,7 @@ pb_settings->set_cudss_hybrid_mode(settings.cudss_hybrid_mode); pb_settings->set_cudss_hybrid_execute_mode(settings.cudss_hybrid_execute_mode); pb_settings->set_cudss_host_nthreads(settings.cudss_host_nthreads); + pb_settings->set_barrier_iterative_refinement_method(settings.barrier_iterative_refinement_method); pb_settings->set_save_best_primal_so_far(settings.save_best_primal_so_far); pb_settings->set_first_primal_feasible(settings.first_primal_feasible); pb_settings->set_pdlp_precision(static_cast(settings.pdlp_precision)); diff --git a/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc b/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc index 8ae03d9c4f..62ed340501 100644 --- a/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc +++ b/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc @@ -90,6 +90,9 @@ if (pb_settings.has_cudss_host_nthreads()) { settings.cudss_host_nthreads = pb_settings.cudss_host_nthreads(); } + if (pb_settings.has_barrier_iterative_refinement_method()) { + settings.barrier_iterative_refinement_method = pb_settings.barrier_iterative_refinement_method(); + } settings.save_best_primal_so_far = pb_settings.save_best_primal_so_far(); settings.first_primal_feasible = pb_settings.first_primal_feasible(); if (pb_settings.has_pdlp_precision()) { diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index 52881fe805..583b71542f 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -138,6 +138,7 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_ORDERING, &pdlp_settings.ordering, -1, 1, -1}, {CUOPT_CUDSS_ND_NLEVELS, &pdlp_settings.cudss_nd_nlevels, -1, std::numeric_limits::max(), -1}, {CUOPT_CUDSS_HOST_NTHREADS, &pdlp_settings.cudss_host_nthreads, -1, std::numeric_limits::max(), -1}, + {CUOPT_BARRIER_ITERATIVE_REFINEMENT_METHOD, &pdlp_settings.barrier_iterative_refinement_method, 0, 1, 1}, {CUOPT_BARRIER_DUAL_INITIAL_POINT, &pdlp_settings.barrier_dual_initial_point, -1, 1, -1}, {CUOPT_POSTSOLVE_INFO, &pdlp_settings.postsolve_info, -1, 1, -1}, {CUOPT_MIP_CUT_PASSES, &mip_settings.max_cut_passes, -1, std::numeric_limits::max(), 10}, diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index b19b230d6b..69a017b0bc 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -498,21 +498,23 @@ std::tuple, simplex::lp_status_t, f_t, f_t, f_t f_t norm_rhs = vector_norm2(user_problem.rhs); simplex::simplex_solver_settings_t barrier_settings; - barrier_settings.num_gpus = settings.num_gpus; - barrier_settings.time_limit = settings.time_limit; - barrier_settings.iteration_limit = settings.iteration_limit; - barrier_settings.concurrent_halt = settings.concurrent_halt; - barrier_settings.folding = settings.folding; - barrier_settings.augmented = settings.augmented; - barrier_settings.dualize = settings.dualize; - barrier_settings.ordering = settings.ordering; - barrier_settings.barrier_dual_initial_point = settings.barrier_dual_initial_point; - barrier_settings.postsolve_info = settings.postsolve_info; - barrier_settings.barrier = true; - barrier_settings.barrier_presolve = true; - barrier_settings.crossover = settings.crossover; - barrier_settings.eliminate_dense_columns = settings.eliminate_dense_columns; - barrier_settings.barrier_iterative_refinement = settings.barrier_iterative_refinement; + barrier_settings.num_gpus = settings.num_gpus; + barrier_settings.time_limit = settings.time_limit; + barrier_settings.iteration_limit = settings.iteration_limit; + barrier_settings.concurrent_halt = settings.concurrent_halt; + barrier_settings.folding = settings.folding; + barrier_settings.augmented = settings.augmented; + barrier_settings.dualize = settings.dualize; + barrier_settings.ordering = settings.ordering; + barrier_settings.barrier_dual_initial_point = settings.barrier_dual_initial_point; + barrier_settings.postsolve_info = settings.postsolve_info; + barrier_settings.barrier = true; + barrier_settings.barrier_presolve = true; + barrier_settings.crossover = settings.crossover; + barrier_settings.eliminate_dense_columns = settings.eliminate_dense_columns; + barrier_settings.barrier_iterative_refinement = settings.barrier_iterative_refinement; + barrier_settings.barrier_iterative_refinement_method = + settings.barrier_iterative_refinement_method; barrier_settings.barrier_soc_threshold = settings.barrier_soc_threshold; barrier_settings.barrier_step_scale = settings.barrier_step_scale; barrier_settings.qcqp_ruiz_equilibration = settings.qcqp_ruiz_equilibration; diff --git a/cpp/tests/linear_programming/grpc/grpc_client_test.cpp b/cpp/tests/linear_programming/grpc/grpc_client_test.cpp index 7970863cd8..835d726f00 100644 --- a/cpp/tests/linear_programming/grpc/grpc_client_test.cpp +++ b/cpp/tests/linear_programming/grpc/grpc_client_test.cpp @@ -2248,11 +2248,12 @@ TEST(MapperRoundtrip, PDLPSettingsAllFields) orig.barrier_dual_initial_point = 1; orig.eliminate_dense_columns = true; orig.barrier_iterative_refinement = false; // not the default true, to detect overwrite-on-decode - orig.barrier_step_scale = 0.75; // not the default 0.9 - orig.postsolve_info = 1; - orig.pdlp_precision = pdlp_precision_t::MixedPrecision; - orig.save_best_primal_so_far = true; - orig.first_primal_feasible = true; + orig.barrier_iterative_refinement_method = 0; // not the default 1 (gmres) + orig.barrier_step_scale = 0.75; // not the default 0.9 + orig.postsolve_info = 1; + orig.pdlp_precision = pdlp_precision_t::MixedPrecision; + orig.save_best_primal_so_far = true; + orig.first_primal_feasible = true; cuopt::remote::PDLPSolverSettings pb; map_pdlp_settings_to_proto(orig, &pb); @@ -2293,6 +2294,7 @@ TEST(MapperRoundtrip, PDLPSettingsAllFields) EXPECT_EQ(restored.barrier_dual_initial_point, 1); EXPECT_EQ(restored.eliminate_dense_columns, true); EXPECT_EQ(restored.barrier_iterative_refinement, false); + EXPECT_EQ(restored.barrier_iterative_refinement_method, 0); EXPECT_DOUBLE_EQ(restored.barrier_step_scale, 0.75); EXPECT_EQ(restored.postsolve_info, 1); EXPECT_EQ(restored.pdlp_precision, pdlp_precision_t::MixedPrecision); @@ -2444,6 +2446,7 @@ TEST(MapperRoundtrip, PDLPSettingsDefaultProtoPreservesAllCppDefaults) EXPECT_EQ(after.ordering, fresh.ordering); EXPECT_EQ(after.cudss_nd_nlevels, fresh.cudss_nd_nlevels); EXPECT_EQ(after.cudss_host_nthreads, fresh.cudss_host_nthreads); + EXPECT_EQ(after.barrier_iterative_refinement_method, fresh.barrier_iterative_refinement_method); EXPECT_EQ(after.barrier_dual_initial_point, fresh.barrier_dual_initial_point); EXPECT_DOUBLE_EQ(after.barrier_step_scale, fresh.barrier_step_scale); // Enum-int32 fields (post-decode clamping defends out-of-range; default `0` diff --git a/docs/cuopt/source/convex-settings.rst b/docs/cuopt/source/convex-settings.rst index afc3c9c062..7a513b725b 100644 --- a/docs/cuopt/source/convex-settings.rst +++ b/docs/cuopt/source/convex-settings.rst @@ -420,6 +420,16 @@ Barrier Iterative Refinement .. note:: The default value is ``0`` (off). +Barrier Iterative Refinement Method +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``CUOPT_BARRIER_ITERATIVE_REFINEMENT_METHOD`` controls which iterative refinement method the barrier solver uses when ``CUOPT_BARRIER_ITERATIVE_REFINEMENT`` is enabled. + +* ``0``: Fixed-point +* ``1``: Restarted GMRES (default) + +.. note:: The default value is ``1`` (GMRES). + Barrier Step Scale ^^^^^^^^^^^^^^^^^^^ diff --git a/python/cuopt_server/cuopt_server/tests/test_lp.py b/python/cuopt_server/cuopt_server/tests/test_lp.py index cafc44fb1b..a373399301 100644 --- a/python/cuopt_server/cuopt_server/tests/test_lp.py +++ b/python/cuopt_server/cuopt_server/tests/test_lp.py @@ -143,22 +143,23 @@ def test_sample_milp( @pytest.mark.parametrize( "folding, dualize, ordering, augmented, eliminate_dense, cudss_determ, " "dual_initial_point, cudss_nd_nlevels, cudss_hybrid_mode, " - "cudss_hybrid_execute_mode, cudss_host_nthreads", + "cudss_hybrid_execute_mode, cudss_host_nthreads, " + "barrier_ir_method", [ # Test automatic settings (default) - (-1, -1, -1, -1, True, False, -1, -1, False, False, -1), + (-1, -1, -1, -1, True, False, -1, -1, False, False, -1, 1), # Test folding off, no dualization, cuDSS default ordering, ADAT system - (0, 0, 0, 0, True, False, 0, -1, False, False, -1), + (0, 0, 0, 0, True, False, 0, -1, False, False, -1, 1), # Test folding on, force dualization, AMD ordering, augmented system - (1, 1, 1, 1, True, True, 1, 8, True, True, 4), + (1, 1, 1, 1, True, True, 1, 8, True, True, 4, 0), # Test mixed settings: automatic folding, no dualize, AMD, augmented - (-1, 0, 1, 1, False, False, 0, 4, True, False, 2), + (-1, 0, 1, 1, False, False, 0, 4, True, False, 2, 0), # Test no folding, automatic dualize, cuDSS default, ADAT - (0, -1, 0, 0, True, True, -1, -1, False, False, -1), + (0, -1, 0, 0, True, True, -1, -1, False, False, -1, 1), # Test dual initial point with Lustig-Marsten-Shanno - (-1, -1, -1, -1, True, False, 0, -1, False, False, -1), + (-1, -1, -1, -1, True, False, 0, -1, False, False, -1, 1), # Test dual initial point with least squares - (-1, -1, -1, 1, True, False, 1, -1, False, False, -1), + (-1, -1, -1, 1, True, False, 1, -1, False, False, -1, 0), ], ) def test_barrier_solver_options( @@ -174,6 +175,7 @@ def test_barrier_solver_options( cudss_hybrid_mode, cudss_hybrid_execute_mode, cudss_host_nthreads, + barrier_ir_method, ): """ Test the barrier solver (method=3) with various configuration options: @@ -194,6 +196,8 @@ def test_barrier_solver_options( False to not overlap - cudss_host_nthreads: (-1) unset/automatic, else host worker thread count for cuDSS + - barrier_ir_method: (0) fixed-point residual-correction, (1) restarted + GMRES (default) """ data = get_std_data_for_lp() @@ -214,6 +218,9 @@ def test_barrier_solver_options( cudss_hybrid_execute_mode ) data["solver_config"]["cudss_host_nthreads"] = cudss_host_nthreads + data["solver_config"]["barrier_iterative_refinement_method"] = ( + barrier_ir_method + ) res = get_lp(client, data) @@ -228,7 +235,8 @@ def test_barrier_solver_options( f"cudss_nd_nlevels={cudss_nd_nlevels}, " f"cudss_hybrid_mode={cudss_hybrid_mode}, " f"cudss_hybrid_execute_mode={cudss_hybrid_execute_mode}, " - f"cudss_host_nthreads={cudss_host_nthreads}" + f"cudss_host_nthreads={cudss_host_nthreads}, " + f"barrier_ir_method={barrier_ir_method}" ) print(res.json()) diff --git a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py index 924d5b7339..fd5bf67774 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py @@ -536,6 +536,12 @@ class SolverConfig(BaseModel): "-1 to leave it unset (cuDSS default), or a non-negative thread " "count. Only takes effect when a cuDSS threading layer is loaded", ) + barrier_iterative_refinement_method: Optional[int] = Field( + default=1, + description="Set which iterative refinement method the barrier " + "solver uses. 0 for a fixed-point residual-correction loop, 1 for " + "restarted GMRES (default)", + ) crossover: Optional[bool] = Field( default=False, description="Set True to use crossover, False to not use crossover.", From f5248087dd6cf2e62d82b3be9ea8d885ea814792 Mon Sep 17 00:00:00 2001 From: yuwenchen95 Date: Fri, 14 Aug 2026 07:03:57 -0700 Subject: [PATCH 3/4] Remove less effective parameters Signed-off-by: yuwenchen95 --- .../mathematical_optimization/constants.h | 3 -- .../pdlp/solver_settings.hpp | 3 -- cpp/src/barrier/sparse_cholesky.cuh | 27 -------------- .../dual_simplex/simplex_solver_settings.hpp | 10 +----- cpp/src/grpc/codegen/field_registry.yaml | 12 ++----- .../codegen/generated/cuopt_remote_data.proto | 3 -- .../generated_pdlp_settings_to_proto.inc | 3 -- .../generated_proto_to_pdlp_settings.inc | 5 --- cpp/src/math_optimization/solver_settings.cu | 3 -- cpp/src/pdlp/solve.cu | 3 -- .../grpc/grpc_client_test.cpp | 7 ---- docs/cuopt/source/convex-settings.rst | 32 +---------------- .../cuopt_server/tests/test_lp.py | 35 +++++-------------- .../linear_programming/data_definition.py | 17 --------- 14 files changed, 12 insertions(+), 151 deletions(-) diff --git a/cpp/include/cuopt/mathematical_optimization/constants.h b/cpp/include/cuopt/mathematical_optimization/constants.h index 2a79eece9b..a75daf690c 100644 --- a/cpp/include/cuopt/mathematical_optimization/constants.h +++ b/cpp/include/cuopt/mathematical_optimization/constants.h @@ -54,9 +54,6 @@ #define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns" #define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic" #define CUOPT_CUDSS_ND_NLEVELS "cudss_nd_nlevels" -#define CUOPT_CUDSS_HYBRID_MODE "cudss_hybrid_mode" -#define CUOPT_CUDSS_HYBRID_EXECUTE_MODE "cudss_hybrid_execute_mode" -#define CUOPT_CUDSS_HOST_NTHREADS "cudss_host_nthreads" #define CUOPT_PRESOLVE "presolve" #define CUOPT_MIP_PROBING "mip_probing" #define CUOPT_DUAL_POSTSOLVE "dual_postsolve" diff --git a/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp b/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp index a206111c88..cb5da5a50c 100644 --- a/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp +++ b/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp @@ -291,9 +291,6 @@ class pdlp_solver_settings_t { bool crossover{false}; bool cudss_deterministic{false}; i_t cudss_nd_nlevels{-1}; - bool cudss_hybrid_mode{false}; - bool cudss_hybrid_execute_mode{false}; - i_t cudss_host_nthreads{-1}; i_t folding{-1}; i_t augmented{-1}; i_t dualize{-1}; diff --git a/cpp/src/barrier/sparse_cholesky.cuh b/cpp/src/barrier/sparse_cholesky.cuh index 4121b58715..086cc6390f 100644 --- a/cpp/src/barrier/sparse_cholesky.cuh +++ b/cpp/src/barrier/sparse_cholesky.cuh @@ -306,33 +306,6 @@ class sparse_cholesky_cudss_t : public sparse_cholesky_base_t { "cudssConfigSet for nd nlevels"); } - if (settings_.cudss_hybrid_mode) { - settings_.log.printf("cuDSS hybrid mode : enabled\n"); - int32_t hybrid_mode = 1; - CUDSS_CALL_AND_CHECK_EXIT( - cudssConfigSet(solverConfig, CUDSS_CONFIG_HYBRID_MODE, &hybrid_mode, sizeof(int32_t)), - status, - "cudssConfigSet for hybrid mode"); - } - - if (settings_.cudss_hybrid_execute_mode) { - settings_.log.printf("cuDSS hybrid execute mode : enabled\n"); - int32_t hybrid_execute_mode = 1; - CUDSS_CALL_AND_CHECK_EXIT( - cudssConfigSet( - solverConfig, CUDSS_CONFIG_HYBRID_EXECUTE_MODE, &hybrid_execute_mode, sizeof(int32_t)), - status, - "cudssConfigSet for hybrid execute mode"); - } - - if (settings_.cudss_host_nthreads >= 0) { - settings_.log.printf("cuDSS host nthreads : %d\n", settings_.cudss_host_nthreads); - int32_t host_nthreads = settings_.cudss_host_nthreads; - CUDSS_CALL_AND_CHECK_EXIT( - cudssConfigSet(solverConfig, CUDSS_CONFIG_HOST_NTHREADS, &host_nthreads, sizeof(int32_t)), - status, - "cudssConfigSet for host nthreads"); - } #endif #if USE_ITERATIVE_REFINEMENT diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index d52dfd031d..292e2b5ec5 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -67,9 +67,6 @@ struct simplex_solver_settings_t { barrier_presolve(false), cudss_deterministic(false), cudss_nd_nlevels(-1), - cudss_hybrid_mode(false), - cudss_hybrid_execute_mode(false), - cudss_host_nthreads(-1), deterministic(false), barrier(false), eliminate_dense_columns(true), @@ -166,12 +163,7 @@ struct simplex_solver_settings_t { bool barrier_presolve; // true to use barrier presolve bool cudss_deterministic; // true to use cuDSS deterministic mode, false for non-deterministic i_t cudss_nd_nlevels; // -1 automatic/unset, else METIS nested-dissection depth for cuDSS - bool cudss_hybrid_mode; // true for hybrid CPU/GPU factor storage in cuDSS - bool cudss_hybrid_execute_mode; // true to overlap CPU/GPU work in cuDSS (only meaningful when - // cudss_hybrid_mode is true) - i_t cudss_host_nthreads; // -1 automatic/unset, else host worker thread count for cuDSS - // (only takes effect when a cuDSS threading layer is loaded) - bool barrier; // true to use barrier method, false to use dual simplex method + bool barrier; // true to use barrier method, false to use dual simplex method bool deterministic; // true to use B&B deterministic mode, false to use non-deterministic mode bool eliminate_dense_columns; // true to eliminate dense columns from A*D*A^T bool barrier_iterative_refinement; // true to use iterative refinement for barrier method diff --git a/cpp/src/grpc/codegen/field_registry.yaml b/cpp/src/grpc/codegen/field_registry.yaml index 740b4de4a2..1daafc23d2 100644 --- a/cpp/src/grpc/codegen/field_registry.yaml +++ b/cpp/src/grpc/codegen/field_registry.yaml @@ -563,16 +563,8 @@ pdlp_settings: field_num: 34 type: int32 optional: true - - cudss_hybrid_mode: - field_num: 35 - type: bool - - cudss_hybrid_execute_mode: - field_num: 36 - type: bool - - cudss_host_nthreads: - field_num: 37 - type: int32 - optional: true + # field_num 35-37 previously used by cudss_hybrid_mode/cudss_hybrid_execute_mode/ + # cudss_host_nthreads, removed — do not reuse these numbers. - barrier_iterative_refinement_method: field_num: 38 type: int32 diff --git a/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto b/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto index 58ad934db6..1dd59b5272 100644 --- a/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto +++ b/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto @@ -195,9 +195,6 @@ message PDLPSolverSettings { optional double barrier_step_scale = 32; optional int32 postsolve_info = 33; optional int32 cudss_nd_nlevels = 34; - bool cudss_hybrid_mode = 35; - bool cudss_hybrid_execute_mode = 36; - optional int32 cudss_host_nthreads = 37; optional int32 barrier_iterative_refinement_method = 38; PDLPWarmStartData warm_start_data = 50; } diff --git a/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc b/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc index 9f04dacb0e..e69b2fd6f1 100644 --- a/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc +++ b/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc @@ -37,9 +37,6 @@ pb_settings->set_barrier_step_scale(settings.barrier_step_scale); pb_settings->set_postsolve_info(settings.postsolve_info); pb_settings->set_cudss_nd_nlevels(settings.cudss_nd_nlevels); - pb_settings->set_cudss_hybrid_mode(settings.cudss_hybrid_mode); - pb_settings->set_cudss_hybrid_execute_mode(settings.cudss_hybrid_execute_mode); - pb_settings->set_cudss_host_nthreads(settings.cudss_host_nthreads); pb_settings->set_barrier_iterative_refinement_method(settings.barrier_iterative_refinement_method); pb_settings->set_save_best_primal_so_far(settings.save_best_primal_so_far); pb_settings->set_first_primal_feasible(settings.first_primal_feasible); diff --git a/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc b/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc index 62ed340501..85b8ba37ba 100644 --- a/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc +++ b/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc @@ -85,11 +85,6 @@ if (pb_settings.has_cudss_nd_nlevels()) { settings.cudss_nd_nlevels = pb_settings.cudss_nd_nlevels(); } - settings.cudss_hybrid_mode = pb_settings.cudss_hybrid_mode(); - settings.cudss_hybrid_execute_mode = pb_settings.cudss_hybrid_execute_mode(); - if (pb_settings.has_cudss_host_nthreads()) { - settings.cudss_host_nthreads = pb_settings.cudss_host_nthreads(); - } if (pb_settings.has_barrier_iterative_refinement_method()) { settings.barrier_iterative_refinement_method = pb_settings.barrier_iterative_refinement_method(); } diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index 583b71542f..9312459131 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -137,7 +137,6 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_DUALIZE, &pdlp_settings.dualize, -1, 1, -1}, {CUOPT_ORDERING, &pdlp_settings.ordering, -1, 1, -1}, {CUOPT_CUDSS_ND_NLEVELS, &pdlp_settings.cudss_nd_nlevels, -1, std::numeric_limits::max(), -1}, - {CUOPT_CUDSS_HOST_NTHREADS, &pdlp_settings.cudss_host_nthreads, -1, std::numeric_limits::max(), -1}, {CUOPT_BARRIER_ITERATIVE_REFINEMENT_METHOD, &pdlp_settings.barrier_iterative_refinement_method, 0, 1, 1}, {CUOPT_BARRIER_DUAL_INITIAL_POINT, &pdlp_settings.barrier_dual_initial_point, -1, 1, -1}, {CUOPT_POSTSOLVE_INFO, &pdlp_settings.postsolve_info, -1, 1, -1}, @@ -207,8 +206,6 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_CROSSOVER, &pdlp_settings.crossover, false}, {CUOPT_ELIMINATE_DENSE_COLUMNS, &pdlp_settings.eliminate_dense_columns, true}, {CUOPT_CUDSS_DETERMINISTIC, &pdlp_settings.cudss_deterministic, false}, - {CUOPT_CUDSS_HYBRID_MODE, &pdlp_settings.cudss_hybrid_mode, false}, - {CUOPT_CUDSS_HYBRID_EXECUTE_MODE, &pdlp_settings.cudss_hybrid_execute_mode, false}, {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true}, {CUOPT_BARRIER_ITERATIVE_REFINEMENT, &pdlp_settings.barrier_iterative_refinement, true}, {CUOPT_MIP_PROBING, &mip_settings.probing, true}, diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 69a017b0bc..e7020bdf4d 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -520,9 +520,6 @@ std::tuple, simplex::lp_status_t, f_t, f_t, f_t barrier_settings.qcqp_ruiz_equilibration = settings.qcqp_ruiz_equilibration; barrier_settings.cudss_deterministic = settings.cudss_deterministic; barrier_settings.cudss_nd_nlevels = settings.cudss_nd_nlevels; - barrier_settings.cudss_hybrid_mode = settings.cudss_hybrid_mode; - barrier_settings.cudss_hybrid_execute_mode = settings.cudss_hybrid_execute_mode; - barrier_settings.cudss_host_nthreads = settings.cudss_host_nthreads; barrier_settings.barrier_relaxed_feasibility_tol = settings.tolerances.relative_primal_tolerance; barrier_settings.barrier_relaxed_optimality_tol = settings.tolerances.relative_dual_tolerance; barrier_settings.barrier_relaxed_complementarity_tol = settings.tolerances.relative_gap_tolerance; diff --git a/cpp/tests/linear_programming/grpc/grpc_client_test.cpp b/cpp/tests/linear_programming/grpc/grpc_client_test.cpp index 835d726f00..8900170984 100644 --- a/cpp/tests/linear_programming/grpc/grpc_client_test.cpp +++ b/cpp/tests/linear_programming/grpc/grpc_client_test.cpp @@ -2238,9 +2238,6 @@ TEST(MapperRoundtrip, PDLPSettingsAllFields) orig.per_constraint_residual = true; orig.cudss_deterministic = true; orig.cudss_nd_nlevels = 8; - orig.cudss_hybrid_mode = true; - orig.cudss_hybrid_execute_mode = true; - orig.cudss_host_nthreads = 4; orig.folding = 1; orig.augmented = 1; orig.dualize = 1; @@ -2284,9 +2281,6 @@ TEST(MapperRoundtrip, PDLPSettingsAllFields) EXPECT_EQ(restored.per_constraint_residual, true); EXPECT_EQ(restored.cudss_deterministic, true); EXPECT_EQ(restored.cudss_nd_nlevels, 8); - EXPECT_EQ(restored.cudss_hybrid_mode, true); - EXPECT_EQ(restored.cudss_hybrid_execute_mode, true); - EXPECT_EQ(restored.cudss_host_nthreads, 4); EXPECT_EQ(restored.folding, 1); EXPECT_EQ(restored.augmented, 1); EXPECT_EQ(restored.dualize, 1); @@ -2445,7 +2439,6 @@ TEST(MapperRoundtrip, PDLPSettingsDefaultProtoPreservesAllCppDefaults) EXPECT_EQ(after.dualize, fresh.dualize); EXPECT_EQ(after.ordering, fresh.ordering); EXPECT_EQ(after.cudss_nd_nlevels, fresh.cudss_nd_nlevels); - EXPECT_EQ(after.cudss_host_nthreads, fresh.cudss_host_nthreads); EXPECT_EQ(after.barrier_iterative_refinement_method, fresh.barrier_iterative_refinement_method); EXPECT_EQ(after.barrier_dual_initial_point, fresh.barrier_dual_initial_point); EXPECT_DOUBLE_EQ(after.barrier_step_scale, fresh.barrier_step_scale); diff --git a/docs/cuopt/source/convex-settings.rst b/docs/cuopt/source/convex-settings.rst index 7a513b725b..d5b58ac61a 100644 --- a/docs/cuopt/source/convex-settings.rst +++ b/docs/cuopt/source/convex-settings.rst @@ -294,36 +294,6 @@ cuDSS Nested-Dissection Levels .. note:: The default value is ``-1`` (unset). -cuDSS Hybrid Mode -""""""""""""""""" - -``CUOPT_CUDSS_HYBRID_MODE`` controls whether cuDSS stores factorization data in host (CPU) memory in addition to GPU memory, instead of GPU-only storage. This is primarily useful when a problem's factors do not fit in GPU memory; for problems that already fit, GPU-only storage is typically faster. - -* ``true``: Use hybrid CPU/GPU factor storage -* ``false``: Use GPU-only factor storage (default) - -.. note:: The default value is ``false``. - -cuDSS Hybrid Execute Mode -"""""""""""""""""""""""""" - -``CUOPT_CUDSS_HYBRID_EXECUTE_MODE`` controls whether cuDSS overlaps CPU and GPU computation while in hybrid mode. This setting only has an effect when ``CUOPT_CUDSS_HYBRID_MODE`` is enabled. - -* ``true``: Overlap CPU/GPU work -* ``false``: Do not overlap CPU/GPU work (default) - -.. note:: The default value is ``false``. - -cuDSS Host Threads -""""""""""""""""""" - -``CUOPT_CUDSS_HOST_NTHREADS`` controls the number of host worker threads cuDSS uses. This only takes effect when a cuDSS threading layer is loaded (via the ``CUDSS_THREADING_LIB`` environment variable or the build-time threading layer default). - -* ``-1``: Leave unset, cuDSS chooses (default) -* Non-negative value: Explicit host thread count - -.. note:: The default value is ``-1`` (unset). - Dual Initial Point """""""""""""""""" @@ -425,7 +395,7 @@ Barrier Iterative Refinement Method ``CUOPT_BARRIER_ITERATIVE_REFINEMENT_METHOD`` controls which iterative refinement method the barrier solver uses when ``CUOPT_BARRIER_ITERATIVE_REFINEMENT`` is enabled. -* ``0``: Fixed-point +* ``0``: Fixed-point * ``1``: Restarted GMRES (default) .. note:: The default value is ``1`` (GMRES). diff --git a/python/cuopt_server/cuopt_server/tests/test_lp.py b/python/cuopt_server/cuopt_server/tests/test_lp.py index a373399301..7ddf1208f4 100644 --- a/python/cuopt_server/cuopt_server/tests/test_lp.py +++ b/python/cuopt_server/cuopt_server/tests/test_lp.py @@ -142,24 +142,22 @@ def test_sample_milp( ) @pytest.mark.parametrize( "folding, dualize, ordering, augmented, eliminate_dense, cudss_determ, " - "dual_initial_point, cudss_nd_nlevels, cudss_hybrid_mode, " - "cudss_hybrid_execute_mode, cudss_host_nthreads, " - "barrier_ir_method", + "dual_initial_point, cudss_nd_nlevels, barrier_ir_method", [ # Test automatic settings (default) - (-1, -1, -1, -1, True, False, -1, -1, False, False, -1, 1), + (-1, -1, -1, -1, True, False, -1, -1, 1), # Test folding off, no dualization, cuDSS default ordering, ADAT system - (0, 0, 0, 0, True, False, 0, -1, False, False, -1, 1), + (0, 0, 0, 0, True, False, 0, -1, 1), # Test folding on, force dualization, AMD ordering, augmented system - (1, 1, 1, 1, True, True, 1, 8, True, True, 4, 0), + (1, 1, 1, 1, True, True, 1, 8, 0), # Test mixed settings: automatic folding, no dualize, AMD, augmented - (-1, 0, 1, 1, False, False, 0, 4, True, False, 2, 0), + (-1, 0, 1, 1, False, False, 0, 4, 0), # Test no folding, automatic dualize, cuDSS default, ADAT - (0, -1, 0, 0, True, True, -1, -1, False, False, -1, 1), + (0, -1, 0, 0, True, True, -1, -1, 1), # Test dual initial point with Lustig-Marsten-Shanno - (-1, -1, -1, -1, True, False, 0, -1, False, False, -1, 1), + (-1, -1, -1, -1, True, False, 0, -1, 1), # Test dual initial point with least squares - (-1, -1, -1, 1, True, False, 1, -1, False, False, -1, 0), + (-1, -1, -1, 1, True, False, 1, -1, 0), ], ) def test_barrier_solver_options( @@ -172,9 +170,6 @@ def test_barrier_solver_options( cudss_determ, dual_initial_point, cudss_nd_nlevels, - cudss_hybrid_mode, - cudss_hybrid_execute_mode, - cudss_host_nthreads, barrier_ir_method, ): """ @@ -190,12 +185,6 @@ def test_barrier_solver_options( (1) dual least squares - cudss_nd_nlevels: (-1) unset/automatic, else METIS nested-dissection depth - - cudss_hybrid_mode: True for hybrid CPU/GPU factor storage, False for - GPU-only - - cudss_hybrid_execute_mode: True to overlap CPU/GPU work in hybrid mode, - False to not overlap - - cudss_host_nthreads: (-1) unset/automatic, else host worker thread - count for cuDSS - barrier_ir_method: (0) fixed-point residual-correction, (1) restarted GMRES (default) """ @@ -213,11 +202,6 @@ def test_barrier_solver_options( data["solver_config"]["cudss_deterministic"] = cudss_determ data["solver_config"]["barrier_dual_initial_point"] = dual_initial_point data["solver_config"]["cudss_nd_nlevels"] = cudss_nd_nlevels - data["solver_config"]["cudss_hybrid_mode"] = cudss_hybrid_mode - data["solver_config"]["cudss_hybrid_execute_mode"] = ( - cudss_hybrid_execute_mode - ) - data["solver_config"]["cudss_host_nthreads"] = cudss_host_nthreads data["solver_config"]["barrier_iterative_refinement_method"] = ( barrier_ir_method ) @@ -233,9 +217,6 @@ def test_barrier_solver_options( print(f"barrier_dual_initial_point={dual_initial_point}") print( f"cudss_nd_nlevels={cudss_nd_nlevels}, " - f"cudss_hybrid_mode={cudss_hybrid_mode}, " - f"cudss_hybrid_execute_mode={cudss_hybrid_execute_mode}, " - f"cudss_host_nthreads={cudss_host_nthreads}, " f"barrier_ir_method={barrier_ir_method}" ) print(res.json()) diff --git a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py index fd5bf67774..f48cf915ef 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py @@ -519,23 +519,6 @@ class SolverConfig(BaseModel): description="Set the METIS nested-dissection depth used by cuDSS. " "-1 to leave it unset (cuDSS default), or a non-negative depth", ) - cudss_hybrid_mode: Optional[bool] = Field( - default=False, - description="Set if cuDSS should use hybrid CPU/GPU factor storage. " - "True to enable hybrid mode, False to keep factors GPU-only", - ) - cudss_hybrid_execute_mode: Optional[bool] = Field( - default=False, - description="Set if cuDSS should overlap CPU/GPU work in hybrid mode. " - "Only meaningful when cudss_hybrid_mode is True. " - "True to overlap CPU/GPU work, False to not overlap", - ) - cudss_host_nthreads: Optional[int] = Field( - default=-1, - description="Set the number of host worker threads cuDSS uses. " - "-1 to leave it unset (cuDSS default), or a non-negative thread " - "count. Only takes effect when a cuDSS threading layer is loaded", - ) barrier_iterative_refinement_method: Optional[int] = Field( default=1, description="Set which iterative refinement method the barrier " From 0eaf352564a6221c85037ea3c4ccb04c0304d843 Mon Sep 17 00:00:00 2001 From: yuwenchen95 Date: Tue, 18 Aug 2026 03:42:48 -0700 Subject: [PATCH 4/4] Make iterative refinement options as ENUM & ND level as a hyperparameters Signed-off-by: yuwenchen95 --- .../mathematical_optimization/constants.h | 9 ++-- .../pdlp/solver_settings.hpp | 4 +- .../utilities/internals.hpp | 14 +++++ cpp/src/barrier/barrier.cu | 32 ++++++----- cpp/src/barrier/iterative_refinement.hpp | 14 ++--- .../dual_simplex/simplex_solver_settings.hpp | 13 +++-- cpp/src/grpc/codegen/field_registry.yaml | 20 +++---- .../codegen/generated/cuopt_remote_data.proto | 3 +- .../generated_pdlp_settings_to_proto.inc | 3 +- .../generated_proto_to_pdlp_settings.inc | 7 +-- cpp/src/math_optimization/solver_settings.cu | 6 +-- cpp/src/pdlp/solve.cu | 53 +++++++++---------- .../grpc/grpc_client_test.cpp | 41 +++----------- cpp/tests/socp/solve_barrier_socp.cu | 29 +++++----- docs/cuopt/source/convex-settings.rst | 19 ++----- .../source/cuopt-c/convex/convex-c-api.rst | 5 +- .../cuopt_server/tests/test_lp.py | 8 ++- .../linear_programming/data_definition.py | 9 ++-- 18 files changed, 126 insertions(+), 163 deletions(-) diff --git a/cpp/include/cuopt/mathematical_optimization/constants.h b/cpp/include/cuopt/mathematical_optimization/constants.h index de8a83f421..af8837617b 100644 --- a/cpp/include/cuopt/mathematical_optimization/constants.h +++ b/cpp/include/cuopt/mathematical_optimization/constants.h @@ -50,11 +50,10 @@ #define CUOPT_POSTSOLVE_INFO "postsolve_info" #define CUOPT_BARRIER_PRESOLVE_BOUND_FREE_VARIABLES "barrier_presolve_bound_free_variables" #define CUOPT_BARRIER_ITERATIVE_REFINEMENT "barrier_iterative_refinement" -#define CUOPT_BARRIER_ITERATIVE_REFINEMENT_METHOD "barrier_iterative_refinement_method" #define CUOPT_BARRIER_STEP_SCALE "barrier_step_scale" #define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns" #define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic" -#define CUOPT_CUDSS_ND_NLEVELS "cudss_nd_nlevels" +#define CUOPT_CUDSS_HYPER_ND_NLEVELS "cudss_hyper_nd_nlevels" #define CUOPT_PRESOLVE "presolve" #define CUOPT_MIP_PROBING "mip_probing" #define CUOPT_DUAL_POSTSOLVE "dual_postsolve" @@ -240,8 +239,10 @@ #define CUOPT_MIP_SCALING_ON 1 #define CUOPT_MIP_SCALING_NO_OBJECTIVE 2 -#define CUOPT_BARRIER_ITERATIVE_REFINEMENT_OFF 0 -#define CUOPT_BARRIER_ITERATIVE_REFINEMENT_ON 1 +/* @brief Iterative refinement for barrier method */ +#define CUOPT_BARRIER_IR_OFF 0 +#define CUOPT_BARRIER_IR_GMRES 1 +#define CUOPT_BARRIER_IR_FIXED_POINT 2 /* @brief Scalar problem attribute selectors * Passed as cuopt_int_t; the valid set depends on the accessor's value type. */ diff --git a/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp b/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp index 6fd8cb80ab..1deb4e95fc 100644 --- a/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp +++ b/cpp/include/cuopt/mathematical_optimization/pdlp/solver_settings.hpp @@ -304,8 +304,8 @@ class pdlp_solver_settings_t { i_t qcqp_ruiz_equilibration{-1}; bool eliminate_dense_columns{true}; pdlp_precision_t pdlp_precision{pdlp_precision_t::DefaultPrecision}; - bool barrier_iterative_refinement{true}; - i_t barrier_iterative_refinement_method{1}; // 0: fixed_point, 1: gmres (default) + // Iterative refinement for barrier method: 0: off, 1: gmres (default), 2: fixed_point + i_t barrier_iterative_refinement{barrier_iterative_refinement_t::GMRES}; i_t barrier_soc_threshold{100}; f_t barrier_step_scale{0.9}; bool save_best_primal_so_far{false}; diff --git a/cpp/include/cuopt/mathematical_optimization/utilities/internals.hpp b/cpp/include/cuopt/mathematical_optimization/utilities/internals.hpp index 25f3f34f7b..2e7a49ab21 100644 --- a/cpp/include/cuopt/mathematical_optimization/utilities/internals.hpp +++ b/cpp/include/cuopt/mathematical_optimization/utilities/internals.hpp @@ -142,5 +142,19 @@ enum presolver_t : int { PSLP = CUOPT_PRESOLVE_PSLP }; +/** + * @brief Enum representing the iterative refinement method used by the barrier + * solver after each solve. + * + * Off: Disable iterative refinement. + * GMRES: Use restarted GMRES (default). + * FixedPoint: Use a fixed-point residual-correction loop. + */ +enum barrier_iterative_refinement_t : int { + Off = CUOPT_BARRIER_IR_OFF, + GMRES = CUOPT_BARRIER_IR_GMRES, + FixedPoint = CUOPT_BARRIER_IR_FIXED_POINT +}; + } // namespace mathematical_optimization } // namespace cuopt diff --git a/cpp/src/barrier/barrier.cu b/cpp/src/barrier/barrier.cu index a4eb93eea2..d967d7a96c 100644 --- a/cpp/src/barrier/barrier.cu +++ b/cpp/src/barrier/barrier.cu @@ -2278,10 +2278,13 @@ int barrier_solver_t::initial_point(iteration_data_t& data) } } op(data); - if (settings.barrier_iterative_refinement) { + if (settings.barrier_iterative_refinement != barrier_iterative_refinement_t::Off) { const f_t ir_tol = data.has_sparse_cones() ? f_t(1e-12) : f_t(1e-8); - iterative_refinement( - op, rhs, soln, ir_tol, settings.barrier_iterative_refinement_method); + + const i_t internal_method = + (settings.barrier_iterative_refinement == barrier_iterative_refinement_t::FixedPoint) ? 0 + : 1; + iterative_refinement(op, rhs, soln, ir_tol, internal_method); } for (i_t k = 0; k < lp.num_cols; k++) { @@ -2904,15 +2907,15 @@ i_t barrier_solver_t::gpu_compute_search_direction(iteration_data_tsolve(b, x); } } op(data); - if (settings.barrier_iterative_refinement) { + if (settings.barrier_iterative_refinement != barrier_iterative_refinement_t::Off) { raft::common::nvtx::range fun_scope("Barrier: iterative_refinement"); const f_t ir_tol = data.has_sparse_cones() ? f_t(1e-12) : f_t(1e-8); - const f_t solve_err = - iterative_refinement(op, - data.d_augmented_rhs_, - data.d_augmented_soln_, - ir_tol, - settings.barrier_iterative_refinement_method); + + const i_t internal_method = + (settings.barrier_iterative_refinement == barrier_iterative_refinement_t::FixedPoint) ? 0 + : 1; + const f_t solve_err = iterative_refinement( + op, data.d_augmented_rhs_, data.d_augmented_soln_, ir_tol, internal_method); if (solve_err > 1e-1) { settings.log.printf("|| Aug (dx, dy) - aug_rhs || %e after IR\n", solve_err); } @@ -2982,7 +2985,8 @@ i_t barrier_solver_t::gpu_compute_search_direction(iteration_data_t& data) : data_(data) {} iteration_data_t& data_; @@ -2998,8 +3002,12 @@ i_t barrier_solver_t::gpu_compute_search_direction(iteration_data_t( - adat_op, data.d_h_, data.d_dy_, f_t(1e-8), settings.barrier_iterative_refinement_method); + adat_op, data.d_h_, data.d_dy_, f_t(1e-8), internal_method); if (adat_solve_err > 1e-1) { settings.log.printf("||ADAT*dy - h|| %e after IR\n", adat_solve_err); } diff --git a/cpp/src/barrier/iterative_refinement.hpp b/cpp/src/barrier/iterative_refinement.hpp index 92fd7e57d0..9ef957abd2 100644 --- a/cpp/src/barrier/iterative_refinement.hpp +++ b/cpp/src/barrier/iterative_refinement.hpp @@ -362,11 +362,8 @@ f_t iterative_refinement_gmres(T& op, } template -f_t iterative_refinement(T& op, - const dense_vector_t& b, - dense_vector_t& x, - f_t tol = 1e-8, - i_t method = 1) +f_t iterative_refinement( + T& op, const dense_vector_t& b, dense_vector_t& x, f_t tol, i_t method) { rmm::device_uvector d_b(b.size(), op.data_.handle_ptr->get_stream()); raft::copy(d_b.data(), b.data(), b.size(), op.data_.handle_ptr->get_stream()); @@ -382,11 +379,8 @@ f_t iterative_refinement(T& op, } template -f_t iterative_refinement(T& op, - const rmm::device_uvector& b, - rmm::device_uvector& x, - f_t tol = 1e-8, - i_t method = 1) +f_t iterative_refinement( + T& op, const rmm::device_uvector& b, rmm::device_uvector& x, f_t tol, i_t method) { return (method == 0) ? iterative_refinement_fixed_point(op, b, x, tol) : iterative_refinement_gmres(op, b, x, tol); diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 1b47b7f2d9..b31c960b51 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -70,8 +71,7 @@ struct simplex_solver_settings_t { deterministic(false), barrier(false), eliminate_dense_columns(true), - barrier_iterative_refinement(true), - barrier_iterative_refinement_method(1), + barrier_iterative_refinement(barrier_iterative_refinement_t::GMRES), barrier_step_scale(0.9), barrier_soc_threshold(100), num_gpus(1), @@ -166,11 +166,10 @@ struct simplex_solver_settings_t { i_t cudss_nd_nlevels; // -1 automatic/unset, else METIS nested-dissection depth for cuDSS bool barrier; // true to use barrier method, false to use dual simplex method bool deterministic; // true to use B&B deterministic mode, false to use non-deterministic mode - bool eliminate_dense_columns; // true to eliminate dense columns from A*D*A^T - bool barrier_iterative_refinement; // true to use iterative refinement for barrier method - i_t barrier_iterative_refinement_method; // 0: fixed_point, 1: gmres - f_t barrier_step_scale; // step scale for barrier method - i_t barrier_soc_threshold; // SOC dimension above which rank-2 sparse scaling is used + bool eliminate_dense_columns; // true to eliminate dense columns from A*D*A^T + i_t barrier_iterative_refinement; // 0: off, 1: gmres (default), 2: fixed_point + f_t barrier_step_scale; // step scale for barrier method + i_t barrier_soc_threshold; // SOC dimension above which rank-2 sparse scaling is used int num_gpus; // Number of GPUs to use (maximum of 2 gpus are supported at the moment) i_t folding; // -1 automatic, 0 don't fold, 1 fold i_t augmented; // -1 automatic, 0 to solve with ADAT, 1 to solve with augmented system diff --git a/cpp/src/grpc/codegen/field_registry.yaml b/cpp/src/grpc/codegen/field_registry.yaml index 1daafc23d2..3719f038cd 100644 --- a/cpp/src/grpc/codegen/field_registry.yaml +++ b/cpp/src/grpc/codegen/field_registry.yaml @@ -496,8 +496,7 @@ pdlp_settings: - dual_postsolve: # C++ default is `true`; using proto3 `optional` so that a client that # omits this field gets the solver default rather than the proto3 zero - # (`false`). See also the matching `optional` markers on - # `barrier_iterative_refinement` (pdlp) and `probing` (mip). + # (`false`). See also the matching `optional` marker on `probing` (mip). field_num: 17 type: bool optional: true @@ -539,15 +538,6 @@ pdlp_settings: field_num: 27 type: bool optional: true - - barrier_iterative_refinement: - # Whether the barrier method runs iterative refinement after each solve - # (see cpp/src/barrier/barrier.cu). C++ default is `true`. Declared as - # proto3 `optional` so that a client which omits this field preserves - # the solver default; without `optional`, the proto3 wire zero (`false`) - # would silently overwrite the C++ default. - field_num: 31 - type: bool - optional: true - barrier_step_scale: # Step scale used by the barrier method primal/dual updates. Local-solve # binding restricts the range to [0.5, 0.9999] with default 0.9; see @@ -565,7 +555,10 @@ pdlp_settings: optional: true # field_num 35-37 previously used by cudss_hybrid_mode/cudss_hybrid_execute_mode/ # cudss_host_nthreads, removed — do not reuse these numbers. - - barrier_iterative_refinement_method: + # field_num 31 previously held `barrier_iterative_refinement` as a bool; that field + # was retired and the name reused below (field_num 38) for the merged int method, + # so the exposed parameter name is unchanged even though the wire field_num moved. + - barrier_iterative_refinement: field_num: 38 type: int32 optional: true @@ -736,8 +729,7 @@ mip_settings: # C++ default is `true`; declared as proto3 `optional` so that a client # which omits this field preserves the solver default. Without # `optional`, the proto3 wire zero (`false`) would silently overwrite - # the C++ default. See also `dual_postsolve` and - # `barrier_iterative_refinement` in pdlp_settings. + # the C++ default. See also `dual_postsolve` in pdlp_settings. field_num: 29 type: bool optional: true diff --git a/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto b/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto index 1dd59b5272..469e1ab960 100644 --- a/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto +++ b/cpp/src/grpc/codegen/generated/cuopt_remote_data.proto @@ -191,11 +191,10 @@ message PDLPSolverSettings { bool save_best_primal_so_far = 28; bool first_primal_feasible = 29; optional int32 pdlp_precision = 30; - optional bool barrier_iterative_refinement = 31; optional double barrier_step_scale = 32; optional int32 postsolve_info = 33; optional int32 cudss_nd_nlevels = 34; - optional int32 barrier_iterative_refinement_method = 38; + optional int32 barrier_iterative_refinement = 38; PDLPWarmStartData warm_start_data = 50; } diff --git a/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc b/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc index e69b2fd6f1..1e78625f8c 100644 --- a/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc +++ b/cpp/src/grpc/codegen/generated/generated_pdlp_settings_to_proto.inc @@ -33,11 +33,10 @@ pb_settings->set_ordering(settings.ordering); pb_settings->set_barrier_dual_initial_point(settings.barrier_dual_initial_point); pb_settings->set_eliminate_dense_columns(settings.eliminate_dense_columns); - pb_settings->set_barrier_iterative_refinement(settings.barrier_iterative_refinement); pb_settings->set_barrier_step_scale(settings.barrier_step_scale); pb_settings->set_postsolve_info(settings.postsolve_info); pb_settings->set_cudss_nd_nlevels(settings.cudss_nd_nlevels); - pb_settings->set_barrier_iterative_refinement_method(settings.barrier_iterative_refinement_method); + pb_settings->set_barrier_iterative_refinement(settings.barrier_iterative_refinement); pb_settings->set_save_best_primal_so_far(settings.save_best_primal_so_far); pb_settings->set_first_primal_feasible(settings.first_primal_feasible); pb_settings->set_pdlp_precision(static_cast(settings.pdlp_precision)); diff --git a/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc b/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc index 85b8ba37ba..d02c2139ea 100644 --- a/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc +++ b/cpp/src/grpc/codegen/generated/generated_proto_to_pdlp_settings.inc @@ -73,9 +73,6 @@ if (pb_settings.has_eliminate_dense_columns()) { settings.eliminate_dense_columns = pb_settings.eliminate_dense_columns(); } - if (pb_settings.has_barrier_iterative_refinement()) { - settings.barrier_iterative_refinement = pb_settings.barrier_iterative_refinement(); - } if (pb_settings.has_barrier_step_scale()) { settings.barrier_step_scale = pb_settings.barrier_step_scale(); } @@ -85,8 +82,8 @@ if (pb_settings.has_cudss_nd_nlevels()) { settings.cudss_nd_nlevels = pb_settings.cudss_nd_nlevels(); } - if (pb_settings.has_barrier_iterative_refinement_method()) { - settings.barrier_iterative_refinement_method = pb_settings.barrier_iterative_refinement_method(); + if (pb_settings.has_barrier_iterative_refinement()) { + settings.barrier_iterative_refinement = pb_settings.barrier_iterative_refinement(); } settings.save_best_primal_so_far = pb_settings.save_best_primal_so_far(); settings.first_primal_feasible = pb_settings.first_primal_feasible(); diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index 294f10ec79..79cb3e858e 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -136,8 +136,7 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_FOLDING, &pdlp_settings.folding, -1, 1, -1}, {CUOPT_DUALIZE, &pdlp_settings.dualize, -1, 1, -1}, {CUOPT_ORDERING, &pdlp_settings.ordering, -1, 1, -1}, - {CUOPT_CUDSS_ND_NLEVELS, &pdlp_settings.cudss_nd_nlevels, -1, std::numeric_limits::max(), -1}, - {CUOPT_BARRIER_ITERATIVE_REFINEMENT_METHOD, &pdlp_settings.barrier_iterative_refinement_method, 0, 1, 1}, + {CUOPT_BARRIER_ITERATIVE_REFINEMENT, &pdlp_settings.barrier_iterative_refinement, CUOPT_BARRIER_IR_OFF, CUOPT_BARRIER_IR_FIXED_POINT, CUOPT_BARRIER_IR_GMRES}, {CUOPT_BARRIER_DUAL_INITIAL_POINT, &pdlp_settings.barrier_dual_initial_point, -1, 1, -1}, {CUOPT_POSTSOLVE_INFO, &pdlp_settings.postsolve_info, -1, 1, -1}, {CUOPT_MIP_CUT_PASSES, &mip_settings.max_cut_passes, -1, std::numeric_limits::max(), 10}, @@ -192,6 +191,8 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_BARRIER_PRESOLVE_BOUND_FREE_VARIABLES, &pdlp_settings.barrier_presolve_bound_free_variables, -1, 1, -1, "Bound free variables during barrier presolve: -1 automatic (current default behavior), 0 disabled, 1 enabled"}, // QCQP (barrier) scaling hyper-parameter {CUOPT_QCQP_HYPER_RUIZ_EQUILIBRATION, &pdlp_settings.qcqp_ruiz_equilibration, -1, 1, -1, "Ruiz equilibration for QCQP barrier scaling: -1 automatic (row/column imbalance heuristic), 0 disabled, 1 enabled"}, + // cuDSS (barrier) reordering hyper-parameter + {CUOPT_CUDSS_HYPER_ND_NLEVELS, &pdlp_settings.cudss_nd_nlevels, -1, std::numeric_limits::max(), -1, "METIS nested-dissection depth for cuDSS: -1 unset (cuDSS default), else explicit depth"}, }; // Bool parameters @@ -208,7 +209,6 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_ELIMINATE_DENSE_COLUMNS, &pdlp_settings.eliminate_dense_columns, true}, {CUOPT_CUDSS_DETERMINISTIC, &pdlp_settings.cudss_deterministic, false}, {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true}, - {CUOPT_BARRIER_ITERATIVE_REFINEMENT, &pdlp_settings.barrier_iterative_refinement, true}, {CUOPT_MIP_PROBING, &mip_settings.probing, true}, {CUOPT_USE_DISTRIBUTED_PDLP, &pdlp_settings.use_distributed_pdlp, false}, // Diving heuristic hyper-parameters (hidden from default --help: name contains "hyper_") diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index ffa56da41a..3ae9f222c1 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -510,13 +510,11 @@ std::tuple, simplex::lp_status_t, f_t, f_t, f_t barrier_settings.postsolve_info = settings.postsolve_info; barrier_settings.barrier_presolve_bound_free_variables = settings.barrier_presolve_bound_free_variables; - barrier_settings.barrier = true; - barrier_settings.barrier_presolve = true; - barrier_settings.crossover = settings.crossover; - barrier_settings.eliminate_dense_columns = settings.eliminate_dense_columns; - barrier_settings.barrier_iterative_refinement = settings.barrier_iterative_refinement; - barrier_settings.barrier_iterative_refinement_method = - settings.barrier_iterative_refinement_method; + barrier_settings.barrier = true; + barrier_settings.barrier_presolve = true; + barrier_settings.crossover = settings.crossover; + barrier_settings.eliminate_dense_columns = settings.eliminate_dense_columns; + barrier_settings.barrier_iterative_refinement = settings.barrier_iterative_refinement; barrier_settings.barrier_soc_threshold = settings.barrier_soc_threshold; barrier_settings.barrier_step_scale = settings.barrier_step_scale; barrier_settings.qcqp_ruiz_equilibration = settings.qcqp_ruiz_equilibration; @@ -693,27 +691,26 @@ static optimization_problem_solution_t run_pdlp_solver_in_fp32( static_cast(settings.tolerances.primal_infeasible_tolerance); fs.tolerances.dual_infeasible_tolerance = static_cast(settings.tolerances.dual_infeasible_tolerance); - fs.detect_infeasibility = settings.detect_infeasibility; - fs.strict_infeasibility = settings.strict_infeasibility; - fs.iteration_limit = settings.iteration_limit; - fs.time_limit = static_cast(settings.time_limit); - fs.pdlp_solver_mode = settings.pdlp_solver_mode; - fs.log_to_console = settings.log_to_console; - fs.log_file = settings.log_file; - fs.per_constraint_residual = settings.per_constraint_residual; - fs.save_best_primal_so_far = settings.save_best_primal_so_far; - fs.first_primal_feasible = settings.first_primal_feasible; - fs.all_primal_feasible = settings.all_primal_feasible; - fs.eliminate_dense_columns = settings.eliminate_dense_columns; - fs.barrier_iterative_refinement = settings.barrier_iterative_refinement; - fs.barrier_step_scale = settings.barrier_step_scale; - fs.pdlp_precision = pdlp_precision_t::DefaultPrecision; - fs.method = method_t::PDLP; - fs.inside_mip = settings.inside_mip; - fs.hyper_params = settings.hyper_params; - fs.presolver = settings.presolver; - fs.num_gpus = settings.num_gpus; - fs.concurrent_halt = settings.concurrent_halt; + fs.detect_infeasibility = settings.detect_infeasibility; + fs.strict_infeasibility = settings.strict_infeasibility; + fs.iteration_limit = settings.iteration_limit; + fs.time_limit = static_cast(settings.time_limit); + fs.pdlp_solver_mode = settings.pdlp_solver_mode; + fs.log_to_console = settings.log_to_console; + fs.log_file = settings.log_file; + fs.per_constraint_residual = settings.per_constraint_residual; + fs.save_best_primal_so_far = settings.save_best_primal_so_far; + fs.first_primal_feasible = settings.first_primal_feasible; + fs.all_primal_feasible = settings.all_primal_feasible; + fs.eliminate_dense_columns = settings.eliminate_dense_columns; + fs.barrier_step_scale = settings.barrier_step_scale; + fs.pdlp_precision = pdlp_precision_t::DefaultPrecision; + fs.method = method_t::PDLP; + fs.inside_mip = settings.inside_mip; + fs.hyper_params = settings.hyper_params; + fs.presolver = settings.presolver; + fs.num_gpus = settings.num_gpus; + fs.concurrent_halt = settings.concurrent_halt; pdlp::pdlp_solver_t solver(float_problem, fs, is_batch_mode); if (settings.inside_mip) { solver.set_inside_mip(true); } diff --git a/cpp/tests/linear_programming/grpc/grpc_client_test.cpp b/cpp/tests/linear_programming/grpc/grpc_client_test.cpp index 8900170984..dfb661a241 100644 --- a/cpp/tests/linear_programming/grpc/grpc_client_test.cpp +++ b/cpp/tests/linear_programming/grpc/grpc_client_test.cpp @@ -2244,13 +2244,12 @@ TEST(MapperRoundtrip, PDLPSettingsAllFields) orig.ordering = 2; orig.barrier_dual_initial_point = 1; orig.eliminate_dense_columns = true; - orig.barrier_iterative_refinement = false; // not the default true, to detect overwrite-on-decode - orig.barrier_iterative_refinement_method = 0; // not the default 1 (gmres) - orig.barrier_step_scale = 0.75; // not the default 0.9 - orig.postsolve_info = 1; - orig.pdlp_precision = pdlp_precision_t::MixedPrecision; - orig.save_best_primal_so_far = true; - orig.first_primal_feasible = true; + orig.barrier_iterative_refinement = 0; // not the default 1 (gmres) + orig.barrier_step_scale = 0.75; // not the default 0.9 + orig.postsolve_info = 1; + orig.pdlp_precision = pdlp_precision_t::MixedPrecision; + orig.save_best_primal_so_far = true; + orig.first_primal_feasible = true; cuopt::remote::PDLPSolverSettings pb; map_pdlp_settings_to_proto(orig, &pb); @@ -2287,8 +2286,7 @@ TEST(MapperRoundtrip, PDLPSettingsAllFields) EXPECT_EQ(restored.ordering, 2); EXPECT_EQ(restored.barrier_dual_initial_point, 1); EXPECT_EQ(restored.eliminate_dense_columns, true); - EXPECT_EQ(restored.barrier_iterative_refinement, false); - EXPECT_EQ(restored.barrier_iterative_refinement_method, 0); + EXPECT_EQ(restored.barrier_iterative_refinement, 0); EXPECT_DOUBLE_EQ(restored.barrier_step_scale, 0.75); EXPECT_EQ(restored.postsolve_info, 1); EXPECT_EQ(restored.pdlp_precision, pdlp_precision_t::MixedPrecision); @@ -2371,28 +2369,6 @@ TEST(MapperRoundtrip, PDLPSettingsDualPostsolveExplicitFalseRoundtrips) EXPECT_FALSE(restored.dual_postsolve); } -TEST(MapperRoundtrip, PDLPSettingsBarrierIterativeRefinementOmittedPreservesDefault) -{ - cuopt::remote::PDLPSolverSettings pb; - - pdlp_solver_settings_t fresh; - ASSERT_TRUE(fresh.barrier_iterative_refinement); - map_proto_to_pdlp_settings(pb, fresh); - EXPECT_TRUE(fresh.barrier_iterative_refinement) - << "Omitted optional bool must preserve the C++ default `true`"; -} - -TEST(MapperRoundtrip, PDLPSettingsBarrierIterativeRefinementExplicitFalseRoundtrips) -{ - cuopt::remote::PDLPSolverSettings pb; - pb.set_barrier_iterative_refinement(false); - ASSERT_TRUE(pb.has_barrier_iterative_refinement()); - - pdlp_solver_settings_t restored; - map_proto_to_pdlp_settings(pb, restored); - EXPECT_FALSE(restored.barrier_iterative_refinement); -} - // Wide-coverage sanity: a default-constructed proto (no fields touched on the // wire) must, after the mapper, leave every C++ scalar settings field at its // in-class default. Spot-checks a representative cross-section of the fields @@ -2431,7 +2407,6 @@ TEST(MapperRoundtrip, PDLPSettingsDefaultProtoPreservesAllCppDefaults) EXPECT_EQ(after.log_to_console, fresh.log_to_console); EXPECT_EQ(after.dual_postsolve, fresh.dual_postsolve); EXPECT_EQ(after.eliminate_dense_columns, fresh.eliminate_dense_columns); - EXPECT_EQ(after.barrier_iterative_refinement, fresh.barrier_iterative_refinement); // Numeric defaults != 0. EXPECT_EQ(after.num_gpus, fresh.num_gpus); EXPECT_EQ(after.folding, fresh.folding); @@ -2439,7 +2414,7 @@ TEST(MapperRoundtrip, PDLPSettingsDefaultProtoPreservesAllCppDefaults) EXPECT_EQ(after.dualize, fresh.dualize); EXPECT_EQ(after.ordering, fresh.ordering); EXPECT_EQ(after.cudss_nd_nlevels, fresh.cudss_nd_nlevels); - EXPECT_EQ(after.barrier_iterative_refinement_method, fresh.barrier_iterative_refinement_method); + EXPECT_EQ(after.barrier_iterative_refinement, fresh.barrier_iterative_refinement); EXPECT_EQ(after.barrier_dual_initial_point, fresh.barrier_dual_initial_point); EXPECT_DOUBLE_EQ(after.barrier_step_scale, fresh.barrier_step_scale); // Enum-int32 fields (post-decode clamping defends out-of-range; default `0` diff --git a/cpp/tests/socp/solve_barrier_socp.cu b/cpp/tests/socp/solve_barrier_socp.cu index 68e2cb2d31..291f8576ea 100644 --- a/cpp/tests/socp/solve_barrier_socp.cu +++ b/cpp/tests/socp/solve_barrier_socp.cu @@ -854,11 +854,10 @@ TEST(barrier, sparse_soc_expansion_solves_large_single_cone) user_problem.var_types.assign(n, variable_type_t::CONTINUOUS); simplex_solver_settings_t settings; - settings.barrier = true; - settings.barrier_presolve = true; - settings.dualize = 0; - settings.barrier_soc_threshold = 4; - settings.barrier_iterative_refinement = true; + settings.barrier = true; + settings.barrier_presolve = true; + settings.dualize = 0; + settings.barrier_soc_threshold = 4; lp_solution_t solution(m, n); auto status = solve_linear_program_with_barrier(user_problem, settings, solution); @@ -932,12 +931,11 @@ TEST(barrier, mixed_dense_and_sparse_soc_blocks) user_problem.var_types.assign(n, variable_type_t::CONTINUOUS); simplex_solver_settings_t settings; - settings.barrier = true; - settings.barrier_presolve = true; - settings.dualize = 0; - settings.scale_columns = true; - settings.barrier_soc_threshold = 4; - settings.barrier_iterative_refinement = true; + settings.barrier = true; + settings.barrier_presolve = true; + settings.dualize = 0; + settings.scale_columns = true; + settings.barrier_soc_threshold = 4; lp_solution_t solution(m, n); auto status = solve_linear_program_with_barrier(user_problem, settings, solution); @@ -1001,11 +999,10 @@ TEST(barrier, sparse_soc_expansion_solves_dim_500_cone) user_problem.var_types.assign(n, variable_type_t::CONTINUOUS); simplex_solver_settings_t settings; - settings.barrier = true; - settings.barrier_presolve = true; - settings.dualize = 0; - settings.barrier_soc_threshold = 5; - settings.barrier_iterative_refinement = true; + settings.barrier = true; + settings.barrier_presolve = true; + settings.dualize = 0; + settings.barrier_soc_threshold = 5; lp_solution_t solution(m, n); auto status = solve_linear_program_with_barrier(user_problem, settings, solution); diff --git a/docs/cuopt/source/convex-settings.rst b/docs/cuopt/source/convex-settings.rst index d5b58ac61a..6b7d237539 100644 --- a/docs/cuopt/source/convex-settings.rst +++ b/docs/cuopt/source/convex-settings.rst @@ -287,7 +287,7 @@ cuDSS Deterministic Mode cuDSS Nested-Dissection Levels """"""""""""""""""""""""""""""" -``CUOPT_CUDSS_ND_NLEVELS`` controls the METIS nested-dissection depth used by cuDSS during reordering. +``CUOPT_CUDSS_HYPER_ND_NLEVELS`` controls the METIS nested-dissection depth used by cuDSS during reordering. * ``-1``: Leave unset, cuDSS chooses (default) * Non-negative value: Explicit nested-dissection depth @@ -383,20 +383,11 @@ The duality gap is computed as follows:: Barrier Iterative Refinement ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``CUOPT_BARRIER_ITERATIVE_REFINEMENT`` controls whether iterative refinement is applied after each barrier iteration to improve solution accuracy. +``CUOPT_BARRIER_ITERATIVE_REFINEMENT`` controls whether, and how, iterative refinement is applied after each barrier iteration to improve solution accuracy. -* ``0`` (``CUOPT_BARRIER_ITERATIVE_REFINEMENT_OFF``): Disable iterative refinement (default). -* ``1`` (``CUOPT_BARRIER_ITERATIVE_REFINEMENT_ON``): Enable iterative refinement. - -.. note:: The default value is ``0`` (off). - -Barrier Iterative Refinement Method -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -``CUOPT_BARRIER_ITERATIVE_REFINEMENT_METHOD`` controls which iterative refinement method the barrier solver uses when ``CUOPT_BARRIER_ITERATIVE_REFINEMENT`` is enabled. - -* ``0``: Fixed-point -* ``1``: Restarted GMRES (default) +* ``0`` (``CUOPT_BARRIER_IR_OFF``): Disable iterative refinement. +* ``1`` (``CUOPT_BARRIER_IR_GMRES``): Restarted GMRES (default). +* ``2`` (``CUOPT_BARRIER_IR_FIXED_POINT``): Fixed-point residual-correction. .. note:: The default value is ``1`` (GMRES). diff --git a/docs/cuopt/source/cuopt-c/convex/convex-c-api.rst b/docs/cuopt/source/cuopt-c/convex/convex-c-api.rst index 31a4516ca0..630374922a 100644 --- a/docs/cuopt/source/cuopt-c/convex/convex-c-api.rst +++ b/docs/cuopt/source/cuopt-c/convex/convex-c-api.rst @@ -258,8 +258,9 @@ Barrier Iterative Refinement Constants These constants are used to configure `CUOPT_BARRIER_ITERATIVE_REFINEMENT` via :c:func:`cuOptSetIntegerParameter`. -.. doxygendefine:: CUOPT_BARRIER_ITERATIVE_REFINEMENT_OFF -.. doxygendefine:: CUOPT_BARRIER_ITERATIVE_REFINEMENT_ON +.. doxygendefine:: CUOPT_BARRIER_IR_OFF +.. doxygendefine:: CUOPT_BARRIER_IR_GMRES +.. doxygendefine:: CUOPT_BARRIER_IR_FIXED_POINT Warm Start diff --git a/python/cuopt_server/cuopt_server/tests/test_lp.py b/python/cuopt_server/cuopt_server/tests/test_lp.py index 7ddf1208f4..3febecc2f9 100644 --- a/python/cuopt_server/cuopt_server/tests/test_lp.py +++ b/python/cuopt_server/cuopt_server/tests/test_lp.py @@ -185,8 +185,8 @@ def test_barrier_solver_options( (1) dual least squares - cudss_nd_nlevels: (-1) unset/automatic, else METIS nested-dissection depth - - barrier_ir_method: (0) fixed-point residual-correction, (1) restarted - GMRES (default) + - barrier_ir_method: (0) off, (1) restarted GMRES (default), (2) + fixed-point residual-correction """ data = get_std_data_for_lp() @@ -202,9 +202,7 @@ def test_barrier_solver_options( data["solver_config"]["cudss_deterministic"] = cudss_determ data["solver_config"]["barrier_dual_initial_point"] = dual_initial_point data["solver_config"]["cudss_nd_nlevels"] = cudss_nd_nlevels - data["solver_config"]["barrier_iterative_refinement_method"] = ( - barrier_ir_method - ) + data["solver_config"]["barrier_iterative_refinement"] = barrier_ir_method res = get_lp(client, data) diff --git a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py index f48cf915ef..ea57b58d6d 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py @@ -519,11 +519,12 @@ class SolverConfig(BaseModel): description="Set the METIS nested-dissection depth used by cuDSS. " "-1 to leave it unset (cuDSS default), or a non-negative depth", ) - barrier_iterative_refinement_method: Optional[int] = Field( + barrier_iterative_refinement: Optional[int] = Field( default=1, - description="Set which iterative refinement method the barrier " - "solver uses. 0 for a fixed-point residual-correction loop, 1 for " - "restarted GMRES (default)", + description="Set whether, and how, the barrier solver applies " + "iterative refinement after each solve. 0 to disable, 1 for " + "restarted GMRES (default), 2 for a fixed-point residual-correction " + "loop", ) crossover: Optional[bool] = Field( default=False,