diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 799eb291f..447b03a32 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,6 +56,11 @@ repos: - repo: https://github.com/rapidsai/pre-commit-hooks rev: v1.2.1 hooks: + - id: verify-alpha-spec + args: + - --fix + - --mode + - release - id: verify-copyright args: [--fix, --spdx] files: | @@ -83,7 +88,5 @@ repos: entry: python ci/utils/update_doc_versions.py language: system files: docs/cuopt/source/versions1.json - - default_language_version: - python: python3 + python: python3 diff --git a/conda/environments/all_cuda-129_arch-aarch64.yaml b/conda/environments/all_cuda-129_arch-aarch64.yaml index 59d6b43c0..7a4a54a1d 100644 --- a/conda/environments/all_cuda-129_arch-aarch64.yaml +++ b/conda/environments/all_cuda-129_arch-aarch64.yaml @@ -19,7 +19,7 @@ dependencies: - cuda-python>=12.9.2,<13.0a0 - cuda-sanitizer-api - cuda-version=12.9 -- cudf==25.12.*,>=0.0.0a0 +- cudf==25.12.* - cupy>=13.6.0 - cxx-compiler - cython>=3.0.3 @@ -35,8 +35,8 @@ dependencies: - libcurand-dev - libcusolver-dev - libcusparse-dev -- libraft-headers==25.12.*,>=0.0.0a0 -- librmm==25.12.*,>=0.0.0a0 +- libraft-headers==25.12.* +- librmm==25.12.* - make - msgpack-numpy==0.4.8 - msgpack-python==1.1.0 @@ -53,16 +53,16 @@ dependencies: - pip - pre-commit - psutil>=6.0.0 -- pylibraft==25.12.*,>=0.0.0a0 +- pylibraft==25.12.* - pyrsistent - pytest-cov - pytest<8 - python>=3.10,<3.14 - rapids-build-backend>=0.4.0,<0.5.0.dev0 -- rapids-dask-dependency==25.12.*,>=0.0.0a0 -- rapids-logger==0.2.*,>=0.0.0a0 +- rapids-dask-dependency==25.12.* +- rapids-logger==0.2.* - requests -- rmm==25.12.*,>=0.0.0a0 +- rmm==25.12.* - scikit-build-core>=0.10.0 - sphinx - sphinx-copybutton diff --git a/conda/environments/all_cuda-129_arch-x86_64.yaml b/conda/environments/all_cuda-129_arch-x86_64.yaml index 4aad50d00..6c5ae382e 100644 --- a/conda/environments/all_cuda-129_arch-x86_64.yaml +++ b/conda/environments/all_cuda-129_arch-x86_64.yaml @@ -19,7 +19,7 @@ dependencies: - cuda-python>=12.9.2,<13.0a0 - cuda-sanitizer-api - cuda-version=12.9 -- cudf==25.12.*,>=0.0.0a0 +- cudf==25.12.* - cupy>=13.6.0 - cxx-compiler - cython>=3.0.3 @@ -35,8 +35,8 @@ dependencies: - libcurand-dev - libcusolver-dev - libcusparse-dev -- libraft-headers==25.12.*,>=0.0.0a0 -- librmm==25.12.*,>=0.0.0a0 +- libraft-headers==25.12.* +- librmm==25.12.* - make - msgpack-numpy==0.4.8 - msgpack-python==1.1.0 @@ -53,16 +53,16 @@ dependencies: - pip - pre-commit - psutil>=6.0.0 -- pylibraft==25.12.*,>=0.0.0a0 +- pylibraft==25.12.* - pyrsistent - pytest-cov - pytest<8 - python>=3.10,<3.14 - rapids-build-backend>=0.4.0,<0.5.0.dev0 -- rapids-dask-dependency==25.12.*,>=0.0.0a0 -- rapids-logger==0.2.*,>=0.0.0a0 +- rapids-dask-dependency==25.12.* +- rapids-logger==0.2.* - requests -- rmm==25.12.*,>=0.0.0a0 +- rmm==25.12.* - scikit-build-core>=0.10.0 - sphinx - sphinx-copybutton diff --git a/conda/environments/all_cuda-130_arch-aarch64.yaml b/conda/environments/all_cuda-130_arch-aarch64.yaml index 5adb8aeae..82c250f01 100644 --- a/conda/environments/all_cuda-130_arch-aarch64.yaml +++ b/conda/environments/all_cuda-130_arch-aarch64.yaml @@ -19,7 +19,7 @@ dependencies: - cuda-python>=13.0.1,<14.0a0 - cuda-sanitizer-api - cuda-version=13.0 -- cudf==25.12.*,>=0.0.0a0 +- cudf==25.12.* - cupy>=13.6.0 - cxx-compiler - cython>=3.0.3 @@ -35,8 +35,8 @@ dependencies: - libcurand-dev - libcusolver-dev - libcusparse-dev -- libraft-headers==25.12.*,>=0.0.0a0 -- librmm==25.12.*,>=0.0.0a0 +- libraft-headers==25.12.* +- librmm==25.12.* - make - msgpack-numpy==0.4.8 - msgpack-python==1.1.0 @@ -53,16 +53,16 @@ dependencies: - pip - pre-commit - psutil>=6.0.0 -- pylibraft==25.12.*,>=0.0.0a0 +- pylibraft==25.12.* - pyrsistent - pytest-cov - pytest<8 - python>=3.10,<3.14 - rapids-build-backend>=0.4.0,<0.5.0.dev0 -- rapids-dask-dependency==25.12.*,>=0.0.0a0 -- rapids-logger==0.2.*,>=0.0.0a0 +- rapids-dask-dependency==25.12.* +- rapids-logger==0.2.* - requests -- rmm==25.12.*,>=0.0.0a0 +- rmm==25.12.* - scikit-build-core>=0.10.0 - sphinx - sphinx-copybutton diff --git a/conda/environments/all_cuda-130_arch-x86_64.yaml b/conda/environments/all_cuda-130_arch-x86_64.yaml index 11c24d589..e89e322d6 100644 --- a/conda/environments/all_cuda-130_arch-x86_64.yaml +++ b/conda/environments/all_cuda-130_arch-x86_64.yaml @@ -19,7 +19,7 @@ dependencies: - cuda-python>=13.0.1,<14.0a0 - cuda-sanitizer-api - cuda-version=13.0 -- cudf==25.12.*,>=0.0.0a0 +- cudf==25.12.* - cupy>=13.6.0 - cxx-compiler - cython>=3.0.3 @@ -35,8 +35,8 @@ dependencies: - libcurand-dev - libcusolver-dev - libcusparse-dev -- libraft-headers==25.12.*,>=0.0.0a0 -- librmm==25.12.*,>=0.0.0a0 +- libraft-headers==25.12.* +- librmm==25.12.* - make - msgpack-numpy==0.4.8 - msgpack-python==1.1.0 @@ -53,16 +53,16 @@ dependencies: - pip - pre-commit - psutil>=6.0.0 -- pylibraft==25.12.*,>=0.0.0a0 +- pylibraft==25.12.* - pyrsistent - pytest-cov - pytest<8 - python>=3.10,<3.14 - rapids-build-backend>=0.4.0,<0.5.0.dev0 -- rapids-dask-dependency==25.12.*,>=0.0.0a0 -- rapids-logger==0.2.*,>=0.0.0a0 +- rapids-dask-dependency==25.12.* +- rapids-logger==0.2.* - requests -- rmm==25.12.*,>=0.0.0a0 +- rmm==25.12.* - scikit-build-core>=0.10.0 - sphinx - sphinx-copybutton diff --git a/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp b/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp index ea697e1e1..76388504e 100644 --- a/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp @@ -212,7 +212,7 @@ class pdlp_solver_settings_t { method_t method{method_t::Concurrent}; bool inside_mip{false}; // For concurrent termination - volatile int* concurrent_halt{nullptr}; + std::atomic* concurrent_halt{nullptr}; static constexpr f_t minimal_absolute_tolerance = 1.0e-12; private: diff --git a/cpp/src/dual_simplex/basis_solves.cpp b/cpp/src/dual_simplex/basis_solves.cpp index db24f55a2..3080f269d 100644 --- a/cpp/src/dual_simplex/basis_solves.cpp +++ b/cpp/src/dual_simplex/basis_solves.cpp @@ -613,6 +613,8 @@ i_t factorize_basis(const csc_matrix_t& A, template i_t basis_repair(const csc_matrix_t& A, const simplex_solver_settings_t& settings, + const std::vector& lower, + const std::vector& upper, const std::vector& deficient, const std::vector& slacks_needed, std::vector& basis_list, @@ -658,7 +660,15 @@ i_t basis_repair(const csc_matrix_t& A, nonbasic_list[nonbasic_map[replace_j]] = bad_j; vstatus[replace_j] = variable_status_t::BASIC; // This is the main issue. What value should bad_j take on. - vstatus[bad_j] = variable_status_t::NONBASIC_FREE; + if (lower[bad_j] == -inf && upper[bad_j] == inf) { + vstatus[bad_j] = variable_status_t::NONBASIC_FREE; + } else if (lower[bad_j] > -inf) { + vstatus[bad_j] = variable_status_t::NONBASIC_LOWER; + } else if (upper[bad_j] < inf) { + vstatus[bad_j] = variable_status_t::NONBASIC_UPPER; + } else { + assert(1 == 0); + } } return 0; @@ -849,6 +859,8 @@ template int factorize_basis(const csc_matrix_t& A, template int basis_repair(const csc_matrix_t& A, const simplex_solver_settings_t& settings, + const std::vector& lower, + const std::vector& upper, const std::vector& deficient, const std::vector& slacks_needed, std::vector& basis_list, diff --git a/cpp/src/dual_simplex/basis_solves.hpp b/cpp/src/dual_simplex/basis_solves.hpp index b668c0f46..0745806a6 100644 --- a/cpp/src/dual_simplex/basis_solves.hpp +++ b/cpp/src/dual_simplex/basis_solves.hpp @@ -42,6 +42,8 @@ i_t factorize_basis(const csc_matrix_t& A, template i_t basis_repair(const csc_matrix_t& A, const simplex_solver_settings_t& settings, + const std::vector& lower, + const std::vector& upper, const std::vector& deficient, const std::vector& slacks_needed, std::vector& basis_list, diff --git a/cpp/src/dual_simplex/basis_updates.cpp b/cpp/src/dual_simplex/basis_updates.cpp index 6b79f3c86..11056a65e 100644 --- a/cpp/src/dual_simplex/basis_updates.cpp +++ b/cpp/src/dual_simplex/basis_updates.cpp @@ -2046,6 +2046,8 @@ template int basis_update_mpf_t::refactor_basis( const csc_matrix_t& A, const simplex_solver_settings_t& settings, + const std::vector& lower, + const std::vector& upper, std::vector& basic_list, std::vector& nonbasic_list, std::vector& vstatus) @@ -2066,7 +2068,7 @@ int basis_update_mpf_t::refactor_basis( deficient, slacks_needed) == -1) { settings.log.debug("Initial factorization failed\n"); - basis_repair(A, settings, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); + basis_repair(A, settings, lower, upper, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); #ifdef CHECK_BASIS_REPAIR const i_t m = A.m; diff --git a/cpp/src/dual_simplex/basis_updates.hpp b/cpp/src/dual_simplex/basis_updates.hpp index cea907074..9b5d3e614 100644 --- a/cpp/src/dual_simplex/basis_updates.hpp +++ b/cpp/src/dual_simplex/basis_updates.hpp @@ -373,6 +373,8 @@ class basis_update_mpf_t { // Compute L*U = A(p, basic_list) int refactor_basis(const csc_matrix_t& A, const simplex_solver_settings_t& settings, + const std::vector& lower, + const std::vector& upper, std::vector& basic_list, std::vector& nonbasic_list, std::vector& vstatus); diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 77acca8f7..6161f4d3f 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -1135,6 +1135,7 @@ void branch_and_bound_t::diving_thread(const csr_matrix_t& A if (get_upper_bound() < start_node->node.lower_bound) { continue; } bool recompute_bounds_and_basis = true; + i_t nodes_explored = 0; search_tree_t subtree(std::move(start_node->node)); std::deque*> stack; stack.push_front(&subtree.root); @@ -1152,6 +1153,8 @@ void branch_and_bound_t::diving_thread(const csr_matrix_t& A if (toc(exploration_stats_.start_time) > settings_.time_limit) { return; } + if (nodes_explored >= 1000) { break; } + node_solve_info_t status = solve_node(node_ptr, subtree, leaf_problem, @@ -1165,6 +1168,8 @@ void branch_and_bound_t::diving_thread(const csr_matrix_t& A start_node->upper, log); + nodes_explored++; + recompute_bounds_and_basis = !has_children(status); if (status == node_solve_info_t::TIME_LIMIT) { diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index 7891711f7..38438cc9e 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -113,7 +113,7 @@ class branch_and_bound_t { f_t get_lower_bound(); i_t get_heap_size(); bool enable_concurrent_lp_root_solve() const { return enable_concurrent_lp_root_solve_; } - volatile int* get_root_concurrent_halt() { return &root_concurrent_halt_; } + std::atomic* get_root_concurrent_halt() { return &root_concurrent_halt_; } void set_root_concurrent_halt(int value) { root_concurrent_halt_ = value; } lp_status_t solve_root_relaxation(simplex_solver_settings_t const& lp_settings); @@ -170,7 +170,7 @@ class branch_and_bound_t { std::vector edge_norms_; std::atomic root_crossover_solution_set_{false}; bool enable_concurrent_lp_root_solve_{false}; - volatile int root_concurrent_halt_{0}; + std::atomic root_concurrent_halt_{0}; // Pseudocosts pseudo_costs_t pc_; diff --git a/cpp/src/dual_simplex/crossover.cpp b/cpp/src/dual_simplex/crossover.cpp index 23d9a0e8e..3dd61b152 100644 --- a/cpp/src/dual_simplex/crossover.cpp +++ b/cpp/src/dual_simplex/crossover.cpp @@ -786,7 +786,7 @@ i_t primal_push(const lp_problem_t& lp, if (rank != m) { settings.log.debug("Failed to factorize basis. rank %d m %d\n", rank, m); basis_repair( - lp.A, settings, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); + lp.A, settings, lp.lower, lp.upper, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); if (factorize_basis( lp.A, settings, basic_list, L, U, p, pinv, q, deficient, slacks_needed) == -1) { settings.log.printf("Failed to factorize basis after repair. rank %d m %d\n", rank, m); @@ -1132,7 +1132,7 @@ crossover_status_t crossover(const lp_problem_t& lp, rank = factorize_basis(lp.A, settings, basic_list, L, U, p, pinv, q, deficient, slacks_needed); if (rank != m) { settings.log.debug("Failed to factorize basis. rank %d m %d\n", rank, m); - basis_repair(lp.A, settings, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); + basis_repair(lp.A, settings, lp.lower, lp.upper, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); if (factorize_basis(lp.A, settings, basic_list, L, U, p, pinv, q, deficient, slacks_needed) == -1) { settings.log.printf("Failed to factorize basis after repair. rank %d m %d\n", rank, m); @@ -1323,7 +1323,7 @@ crossover_status_t crossover(const lp_problem_t& lp, factorize_basis(lp.A, settings, basic_list, L, U, p, pinv, q, deficient, slacks_needed); if (rank != m) { settings.log.debug("Failed to factorize basis. rank %d m %d\n", rank, m); - basis_repair(lp.A, settings, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); + basis_repair(lp.A, settings, lp.lower, lp.upper, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); if (factorize_basis( lp.A, settings, basic_list, L, U, p, pinv, q, deficient, slacks_needed) == -1) { settings.log.printf("Failed to factorize basis after repair. rank %d m %d\n", rank, m); diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 56298ef4d..e0ac7239e 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -623,14 +623,17 @@ f_t compute_initial_primal_infeasibilities(const lp_problem_t& lp, const std::vector& basic_list, const std::vector& x, std::vector& squared_infeasibilities, - std::vector& infeasibility_indices) + std::vector& infeasibility_indices, + f_t& primal_inf) { const i_t m = lp.num_rows; const i_t n = lp.num_cols; - squared_infeasibilities.resize(n, 0.0); + squared_infeasibilities.resize(n); + std::fill(squared_infeasibilities.begin(), squared_infeasibilities.end(), 0.0); infeasibility_indices.reserve(n); infeasibility_indices.clear(); - f_t primal_inf = 0.0; + f_t primal_inf_squared = 0.0; + primal_inf = 0.0; for (i_t k = 0; k < m; ++k) { const i_t j = basic_list[k]; const f_t lower_infeas = lp.lower[j] - x[j]; @@ -640,10 +643,11 @@ f_t compute_initial_primal_infeasibilities(const lp_problem_t& lp, const f_t square_infeas = infeas * infeas; squared_infeasibilities[j] = square_infeas; infeasibility_indices.push_back(j); - primal_inf += square_infeas; + primal_inf_squared += square_infeas; + primal_inf += infeas; } } - return primal_inf; + return primal_inf_squared; } template @@ -2241,7 +2245,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, assert(superbasic_list.size() == 0); assert(nonbasic_list.size() == n - m); - if (ft.refactor_basis(lp.A, settings, basic_list, nonbasic_list, vstatus) > 0) { + if (ft.refactor_basis(lp.A, settings, lp.lower, lp.upper, basic_list, nonbasic_list, vstatus) > 0) { return dual::status_t::NUMERICAL; } @@ -2268,7 +2272,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, #ifdef COMPUTE_DUAL_RESIDUAL std::vector dual_res1; - compute_dual_residual(lp.A, objective, y, z, dual_res1); + phase2::compute_dual_residual(lp.A, objective, y, z, dual_res1); f_t dual_res_norm = vector_norm_inf(dual_res1); if (dual_res_norm > settings.tight_tol) { settings.log.printf("|| A'*y + z - c || %e\n", dual_res_norm); @@ -2357,8 +2361,9 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, std::vector bounded_variables(n, 0); phase2::compute_bounded_info(lp.lower, lp.upper, bounded_variables); - f_t primal_infeasibility = phase2::compute_initial_primal_infeasibilities( - lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); + f_t primal_infeasibility; + f_t primal_infeasibility_squared = phase2::compute_initial_primal_infeasibilities( + lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices, primal_infeasibility); #ifdef CHECK_BASIC_INFEASIBILITIES phase2::check_basic_infeasibilities(basic_list, basic_mark, infeasibility_indices, 0); @@ -2557,8 +2562,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, phase2::compute_primal_solution_from_basis( lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); x = unperturbed_x; - primal_infeasibility = phase2::compute_initial_primal_infeasibilities( - lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); + primal_infeasibility_squared = phase2::compute_initial_primal_infeasibilities( + lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices, primal_infeasibility); settings.log.printf("Updated primal infeasibility: %e\n", primal_infeasibility); objective = lp.objective; @@ -2594,8 +2599,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, phase2::compute_primal_solution_from_basis( lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); x = unperturbed_x; - primal_infeasibility = phase2::compute_initial_primal_infeasibilities( - lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); + primal_infeasibility_squared = phase2::compute_initial_primal_infeasibilities( + lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices, primal_infeasibility); const f_t orig_dual_infeas = phase2::dual_infeasibility( lp, settings, vstatus, z, settings.tight_tol, settings.dual_tol); @@ -2810,7 +2815,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, delta_xB_0_sparse.i, squared_infeasibilities, infeasibility_indices, - primal_infeasibility); + primal_infeasibility_squared); // Update primal infeasibilities due to changes in basic variables // from the leaving and entering variables phase2::update_primal_infeasibilities(lp, @@ -2822,7 +2827,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, scaled_delta_xB_sparse.i, squared_infeasibilities, infeasibility_indices, - primal_infeasibility); + primal_infeasibility_squared); // Update the entering variable phase2::update_single_primal_infeasibility(lp.lower, lp.upper, @@ -2883,14 +2888,14 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, #endif if (should_refactor) { bool should_recompute_x = false; - if (ft.refactor_basis(lp.A, settings, basic_list, nonbasic_list, vstatus) > 0) { + if (ft.refactor_basis(lp.A, settings, lp.lower, lp.upper, basic_list, nonbasic_list, vstatus) > 0) { should_recompute_x = true; settings.log.printf("Failed to factorize basis. Iteration %d\n", iter); if (toc(start_time) > settings.time_limit) { return dual::status_t::TIME_LIMIT; } i_t count = 0; i_t deficient_size; while ((deficient_size = - ft.refactor_basis(lp.A, settings, basic_list, nonbasic_list, vstatus)) > 0) { + ft.refactor_basis(lp.A, settings, lp.lower, lp.upper, basic_list, nonbasic_list, vstatus)) > 0) { settings.log.printf("Failed to repair basis. Iteration %d. %d deficient columns.\n", iter, static_cast(deficient_size)); @@ -2912,8 +2917,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); x = unperturbed_x; } - phase2::compute_initial_primal_infeasibilities( - lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); + primal_infeasibility_squared = phase2::compute_initial_primal_infeasibilities( + lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices, primal_infeasibility); } #ifdef CHECK_BASIC_INFEASIBILITIES phase2::check_basic_infeasibilities(basic_list, basic_mark, infeasibility_indices, 7); @@ -2951,7 +2956,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, iter, compute_user_objective(lp, obj), infeasibility_indices.size(), - primal_infeasibility, + primal_infeasibility_squared, sum_perturb, now); } diff --git a/cpp/src/dual_simplex/primal.cpp b/cpp/src/dual_simplex/primal.cpp index 80406dcf0..445177fac 100644 --- a/cpp/src/dual_simplex/primal.cpp +++ b/cpp/src/dual_simplex/primal.cpp @@ -298,7 +298,7 @@ primal::status_t primal_phase2(i_t phase, factorize_basis(lp.A, settings, basic_list, L, U, p, pinv, q, deficient, slacks_needed); if (rank != m) { settings.log.debug("Failed to factorize basis. rank %d m %d\n", rank, m); - basis_repair(lp.A, settings, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); + basis_repair(lp.A, settings, lp.lower, lp.upper, deficient, slacks_needed, basic_list, nonbasic_list, vstatus); if (factorize_basis(lp.A, settings, basic_list, L, U, p, pinv, q, deficient, slacks_needed) == -1) { settings.log.printf("Failed to factorize basis after repair. rank %d m %d\n", rank, m); diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 98be9d4cb..a1cc049e7 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -145,8 +145,8 @@ struct simplex_solver_settings_t { std::function heuristic_preemption_callback; std::function&, std::vector&, f_t)> set_simplex_solution_callback; mutable logger_t log; - volatile int* concurrent_halt; // if nullptr ignored, if !nullptr, 0 if solver should - // continue, 1 if solver should halt + std::atomic* concurrent_halt; // if nullptr ignored, if !nullptr, 0 if solver should + // continue, 1 if solver should halt }; } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index ed141a0c4..ab418bf5a 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -306,7 +306,7 @@ void setup_device_symbols(rmm::cuda_stream_view stream_view) detail::set_pdlp_hyper_parameters(stream_view); } -volatile int global_concurrent_halt; +std::atomic global_concurrent_halt{0}; template optimization_problem_solution_t convert_dual_simplex_sol( diff --git a/cpp/src/mip/diversity/diversity_manager.cuh b/cpp/src/mip/diversity/diversity_manager.cuh index 4a78f6cff..9f3b4c90f 100644 --- a/cpp/src/mip/diversity/diversity_manager.cuh +++ b/cpp/src/mip/diversity/diversity_manager.cuh @@ -95,7 +95,7 @@ class diversity_manager_t { // mutex for the simplex solution update std::mutex relaxed_solution_mutex; // atomic for signalling pdlp to stop - volatile int global_concurrent_halt{0}; + std::atomic global_concurrent_halt{0}; rins_t rins; diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cuh b/cpp/src/mip/relaxed_lp/relaxed_lp.cuh index 01931a3dd..0094f5982 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cuh +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cuh @@ -17,14 +17,14 @@ namespace cuopt::linear_programming::detail { struct relaxed_lp_settings_t { - double tolerance = 1e-4; - double time_limit = 1.0; - bool check_infeasibility = true; - bool return_first_feasible = false; - bool save_state = true; - bool per_constraint_residual = true; - bool has_initial_primal = true; - volatile int* concurrent_halt = nullptr; + double tolerance = 1e-4; + double time_limit = 1.0; + bool check_infeasibility = true; + bool return_first_feasible = false; + bool save_state = true; + bool per_constraint_residual = true; + bool has_initial_primal = true; + std::atomic* concurrent_halt = nullptr; }; template diff --git a/cpp/src/utilities/logger.cpp b/cpp/src/utilities/logger.cpp index a16c49c11..217f9c64c 100644 --- a/cpp/src/utilities/logger.cpp +++ b/cpp/src/utilities/logger.cpp @@ -137,9 +137,26 @@ void reset_default_logger() default_logger().flush_on(rapids_logger::level_enum::debug); } +// Guard object whose destructor resets the logger +struct logger_config_guard { + ~logger_config_guard() { cuopt::reset_default_logger(); } +}; + +// Weak reference to detect if any init_logger_t instance is still alive +static std::weak_ptr g_active_guard; +static std::mutex g_guard_mutex; + init_logger_t::init_logger_t(std::string log_file, bool log_to_console) { - // until this function is called, the default sink is the buffer sink + std::lock_guard lock(g_guard_mutex); + + auto existing_guard = g_active_guard.lock(); + if (existing_guard) { + // Reuse existing configuration, just hold a reference to keep it alive + guard_ = existing_guard; + return; + } + cuopt::default_logger().sinks().clear(); // re-initialize sinks @@ -164,8 +181,11 @@ init_logger_t::init_logger_t(std::string log_file, bool log_to_console) for (const auto& entry : buffered_messages) { cuopt::default_logger().log(entry.level, entry.msg.c_str()); } -} -init_logger_t::~init_logger_t() { cuopt::reset_default_logger(); } + // Create guard and store weak reference for future instances to find + auto guard = std::make_shared(); + g_active_guard = guard; + guard_ = guard; +} } // namespace cuopt diff --git a/cpp/src/utilities/logger.hpp b/cpp/src/utilities/logger.hpp index 13c5e36e3..08556a4c7 100644 --- a/cpp/src/utilities/logger.hpp +++ b/cpp/src/utilities/logger.hpp @@ -33,11 +33,13 @@ rapids_logger::logger& default_logger(); */ void reset_default_logger(); +// Ref-counted logger initializer class init_logger_t { + // Using shared_ptr for ref-counting + std::shared_ptr guard_; + public: init_logger_t(std::string log_file, bool log_to_console); - - ~init_logger_t(); }; } // namespace cuopt diff --git a/dependencies.yaml b/dependencies.yaml index 393b99293..c46f11b74 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -297,8 +297,8 @@ dependencies: packages: - boost - cpp-argparse - - librmm==25.12.*,>=0.0.0a0 - - libraft-headers==25.12.*,>=0.0.0a0 + - librmm==25.12.* + - libraft-headers==25.12.* - tbb-devel - zlib - bzip2 @@ -313,7 +313,7 @@ dependencies: common: - output_types: [conda] packages: - - libcuopt-tests==25.12.*,>=0.0.0a0 + - libcuopt-tests==25.12.* build_wheels: common: - output_types: [requirements, pyproject] @@ -350,7 +350,7 @@ dependencies: packages: - numba-cuda>=0.19.1,<0.20.0a0 - numba>=0.60.0 - - rapids-dask-dependency==25.12.*,>=0.0.0a0 + - rapids-dask-dependency==25.12.* - &pandas pandas>=2.0 - output_types: requirements packages: @@ -420,7 +420,7 @@ dependencies: common: - output_types: conda packages: - - &libcuopt_unsuffixed libcuopt==25.12.*,>=0.0.0a0 + - &libcuopt_unsuffixed libcuopt==25.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -433,18 +433,18 @@ dependencies: cuda: "12.*" cuda_suffixed: "true" packages: - - libcuopt-cu12==25.12.*,>=0.0.0a0 + - libcuopt-cu12==25.12.* - matrix: cuda: "13.*" cuda_suffixed: "true" packages: - - libcuopt-cu13==25.12.*,>=0.0.0a0 + - libcuopt-cu13==25.12.* - {matrix: null, packages: [*libcuopt_unsuffixed]} depends_on_cuopt: common: - output_types: conda packages: - - &cuopt_unsuffixed cuopt==25.12.*,>=0.0.0a0 + - &cuopt_unsuffixed cuopt==25.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -457,18 +457,18 @@ dependencies: cuda: "12.*" cuda_suffixed: "true" packages: - - cuopt-cu12==25.12.*,>=0.0.0a0 + - cuopt-cu12==25.12.* - matrix: cuda: "13.*" cuda_suffixed: "true" packages: - - cuopt-cu13==25.12.*,>=0.0.0a0 + - cuopt-cu13==25.12.* - {matrix: null, packages: [*cuopt_unsuffixed]} depends_on_cuopt_server: common: - output_types: conda packages: - - &cuopt_server_unsuffixed cuopt-server==25.12.*,>=0.0.0a0 + - &cuopt_server_unsuffixed cuopt-server==25.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -481,18 +481,18 @@ dependencies: cuda: "12.*" cuda_suffixed: "true" packages: - - cuopt-server-cu12==25.12.*,>=0.0.0a0 + - cuopt-server-cu12==25.12.* - matrix: cuda: "13.*" cuda_suffixed: "true" packages: - - cuopt-server-cu13==25.12.*,>=0.0.0a0 + - cuopt-server-cu13==25.12.* - {matrix: null, packages: [*cuopt_server_unsuffixed]} depends_on_cuopt_sh_client: common: - output_types: [conda, requirements, pyproject] packages: - - &cuopt_sh_client_unsuffixed cuopt-sh-client==25.12.*,>=0.0.0a0 + - &cuopt_sh_client_unsuffixed cuopt-sh-client==25.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -502,7 +502,7 @@ dependencies: common: - output_types: [requirements, pyproject, conda] packages: - - cuopt-mps-parser==25.12.*,>=0.0.0a0 + - cuopt-mps-parser==25.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -512,12 +512,12 @@ dependencies: common: - output_types: conda packages: - - libraft-headers==25.12.*,>=0.0.0a0 + - libraft-headers==25.12.* depends_on_librmm: common: - output_types: conda packages: - - &librmm_unsuffixed librmm==25.12.*,>=0.0.0a0 + - &librmm_unsuffixed librmm==25.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -530,12 +530,12 @@ dependencies: cuda: "12.*" cuda_suffixed: "true" packages: - - librmm-cu12==25.12.*,>=0.0.0a0 + - librmm-cu12==25.12.* - matrix: cuda: "13.*" cuda_suffixed: "true" packages: - - librmm-cu13==25.12.*,>=0.0.0a0 + - librmm-cu13==25.12.* - {matrix: null, packages: [*librmm_unsuffixed]} depends_on_cupy: common: @@ -560,7 +560,7 @@ dependencies: common: - output_types: [conda, requirements, pyproject] packages: - - rapids-logger==0.2.*,>=0.0.0a0 + - rapids-logger==0.2.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -570,7 +570,7 @@ dependencies: common: - output_types: conda packages: - - &rmm_unsuffixed rmm==25.12.*,>=0.0.0a0 + - &rmm_unsuffixed rmm==25.12.* - output_types: requirements packages: # pip recognizes the index as a global option for the requirements.txt file @@ -583,12 +583,12 @@ dependencies: cuda: "12.*" cuda_suffixed: "true" packages: - - rmm-cu12==25.12.*,>=0.0.0a0 + - rmm-cu12==25.12.* - matrix: cuda: "13.*" cuda_suffixed: "true" packages: - - rmm-cu13==25.12.*,>=0.0.0a0 + - rmm-cu13==25.12.* - matrix: packages: - *rmm_unsuffixed @@ -597,7 +597,7 @@ dependencies: common: - output_types: conda packages: - - &cudf_unsuffixed cudf==25.12.*,>=0.0.0a0 + - &cudf_unsuffixed cudf==25.12.* - output_types: requirements packages: - --extra-index-url=https://pypi.nvidia.com @@ -609,12 +609,12 @@ dependencies: cuda: "12.*" cuda_suffixed: "true" packages: - - cudf-cu12==25.12.*,>=0.0.0a0 + - cudf-cu12==25.12.* - matrix: cuda: "13.*" cuda_suffixed: "true" packages: - - cudf-cu13==25.12.*,>=0.0.0a0 + - cudf-cu13==25.12.* - matrix: packages: - *cudf_unsuffixed @@ -623,7 +623,7 @@ dependencies: common: - output_types: conda packages: - - &pylibraft_unsuffixed pylibraft==25.12.*,>=0.0.0a0 + - &pylibraft_unsuffixed pylibraft==25.12.* - output_types: requirements packages: - --extra-index-url=https://pypi.nvidia.com @@ -635,12 +635,12 @@ dependencies: cuda: "12.*" cuda_suffixed: "true" packages: - - pylibraft-cu12==25.12.*,>=0.0.0a0 + - pylibraft-cu12==25.12.* - matrix: cuda: "13.*" cuda_suffixed: "true" packages: - - pylibraft-cu13==25.12.*,>=0.0.0a0 + - pylibraft-cu13==25.12.* - matrix: packages: - *pylibraft_unsuffixed diff --git a/python/cuopt/cuopt/linear_programming/pyproject.toml b/python/cuopt/cuopt/linear_programming/pyproject.toml index c11cd3a58..24907d83f 100644 --- a/python/cuopt/cuopt/linear_programming/pyproject.toml +++ b/python/cuopt/cuopt/linear_programming/pyproject.toml @@ -20,7 +20,7 @@ license = { text = "Apache-2.0" } requires-python = ">=3.10" dependencies = [ "numpy>=1.23.5,<3.0a0", - "rapids-logger==0.2.*,>=0.0.0a0", + "rapids-logger==0.2.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../../../dependencies.yaml and run `rapids-dependency-file-generator`. classifiers = [ "Intended Audience :: Developers", @@ -39,7 +39,7 @@ Source = "https://github.com/nvidia/cuopt" test = [ "pytest-cov", "pytest<8", - "rapids-logger==0.2.*,>=0.0.0a0", + "rapids-logger==0.2.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../../../dependencies.yaml and run `rapids-dependency-file-generator`. [tool.setuptools] @@ -83,5 +83,5 @@ requires = [ "cython>=3.0.3", "ninja", "numpy>=1.23.5,<3.0a0", - "rapids-logger==0.2.*,>=0.0.0a0", + "rapids-logger==0.2.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../../../dependencies.yaml and run `rapids-dependency-file-generator`. diff --git a/python/cuopt/pyproject.toml b/python/cuopt/pyproject.toml index f3f2dbbf8..f27e3f23a 100644 --- a/python/cuopt/pyproject.toml +++ b/python/cuopt/pyproject.toml @@ -20,18 +20,18 @@ license = { text = "Apache-2.0" } requires-python = ">=3.10" dependencies = [ "cuda-python>=13.0.1,<14.0a0", - "cudf==25.12.*,>=0.0.0a0", - "cuopt-mps-parser==25.12.*,>=0.0.0a0", + "cudf==25.12.*", + "cuopt-mps-parser==25.12.*", "cupy-cuda13x>=13.6.0", - "libcuopt==25.12.*,>=0.0.0a0", + "libcuopt==25.12.*", "numba-cuda>=0.19.1,<0.20.0a0", "numba>=0.60.0", "numpy>=1.23.5,<3.0a0", "pandas>=2.0", - "pylibraft==25.12.*,>=0.0.0a0", - "rapids-dask-dependency==25.12.*,>=0.0.0a0", - "rapids-logger==0.2.*,>=0.0.0a0", - "rmm==25.12.*,>=0.0.0a0", + "pylibraft==25.12.*", + "rapids-dask-dependency==25.12.*", + "rapids-logger==0.2.*", + "rmm==25.12.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. classifiers = [ "Intended Audience :: Developers", @@ -48,7 +48,7 @@ test = [ "numpy>=1.23.5,<3.0a0", "pytest-cov", "pytest<8", - "rapids-logger==0.2.*,>=0.0.0a0", + "rapids-logger==0.2.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. [project.urls] @@ -112,12 +112,12 @@ dependencies-file = "../../dependencies.yaml" matrix-entry = "cuda_suffixed=true;use_cuda_wheels=true" requires = [ "cmake>=3.30.4", - "cuopt-mps-parser==25.12.*,>=0.0.0a0", + "cuopt-mps-parser==25.12.*", "cupy-cuda13x>=13.6.0", "cython>=3.0.3", - "libcuopt==25.12.*,>=0.0.0a0", + "libcuopt==25.12.*", "ninja", - "pylibraft==25.12.*,>=0.0.0a0", - "rapids-logger==0.2.*,>=0.0.0a0", - "rmm==25.12.*,>=0.0.0a0", + "pylibraft==25.12.*", + "rapids-logger==0.2.*", + "rmm==25.12.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. diff --git a/python/cuopt_self_hosted/pyproject.toml b/python/cuopt_self_hosted/pyproject.toml index 5b94fb74f..79b2de079 100644 --- a/python/cuopt_self_hosted/pyproject.toml +++ b/python/cuopt_self_hosted/pyproject.toml @@ -19,7 +19,7 @@ authors = [ license = { text = "Apache-2.0" } requires-python = ">=3.10" dependencies = [ - "cuopt-mps-parser==25.12.*,>=0.0.0a0", + "cuopt-mps-parser==25.12.*", "msgpack-numpy==0.4.8", "msgpack==1.1.0", "requests", diff --git a/python/cuopt_server/pyproject.toml b/python/cuopt_server/pyproject.toml index 4e4dc15a2..cbf9429c7 100644 --- a/python/cuopt_server/pyproject.toml +++ b/python/cuopt_server/pyproject.toml @@ -20,7 +20,7 @@ authors = [ license = { text = "Apache-2.0" } requires-python = ">=3.10" dependencies = [ - "cuopt==25.12.*,>=0.0.0a0", + "cuopt==25.12.*", "cupy-cuda13x>=13.6.0", "fastapi", "jsonref==1.1.0", diff --git a/python/libcuopt/pyproject.toml b/python/libcuopt/pyproject.toml index 1fdd8ad2c..5e955f3ba 100644 --- a/python/libcuopt/pyproject.toml +++ b/python/libcuopt/pyproject.toml @@ -31,8 +31,8 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] dependencies = [ - "cuopt-mps-parser==25.12.*,>=0.0.0a0", - "librmm==25.12.*,>=0.0.0a0", + "cuopt-mps-parser==25.12.*", + "librmm==25.12.*", "nvidia-cublas", "nvidia-cudart", "nvidia-cudss", @@ -40,7 +40,7 @@ dependencies = [ "nvidia-cusolver", "nvidia-cusparse", "nvidia-nvtx", - "rapids-logger==0.2.*,>=0.0.0a0", + "rapids-logger==0.2.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. [project.urls] @@ -82,8 +82,8 @@ dependencies-file = "../../dependencies.yaml" matrix-entry = "cuda_suffixed=true;use_cuda_wheels=true" requires = [ "cmake>=3.30.4", - "cuopt-mps-parser==25.12.*,>=0.0.0a0", - "librmm==25.12.*,>=0.0.0a0", + "cuopt-mps-parser==25.12.*", + "librmm==25.12.*", "ninja", - "rapids-logger==0.2.*,>=0.0.0a0", + "rapids-logger==0.2.*", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.