diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 168ffa129..cf6fd6979 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -584,6 +584,7 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& lp_settings.set_log(false); lp_settings.cut_off = upper_bound + settings_.dual_tol; lp_settings.inside_mip = 2; + lp_settings.time_limit = settings_.time_limit - toc(stats_.start_time); // in B&B we only have equality constraints, leave it empty for default std::vector row_sense; @@ -739,7 +740,7 @@ void branch_and_bound_t::exploration_ramp_up(search_tree_t* } } - if (toc(stats_.start_time) > settings_.time_limit) { + if (now > settings_.time_limit) { status_ = mip_exploration_status_t::TIME_LIMIT; return; } @@ -836,9 +837,9 @@ void branch_and_bound_t::explore_subtree(i_t id, } } - if (toc(stats_.start_time) > settings_.time_limit) { + if (now > settings_.time_limit) { status_ = mip_exploration_status_t::TIME_LIMIT; - break; + return; } node_status_t node_status = @@ -846,7 +847,7 @@ void branch_and_bound_t::explore_subtree(i_t id, if (node_status == node_status_t::TIME_LIMIT) { status_ = mip_exploration_status_t::TIME_LIMIT; - break; + return; } else if (node_status == node_status_t::HAS_CHILDREN) { // The stack should only contain the children of the current parent. @@ -972,11 +973,13 @@ void branch_and_bound_t::diving_thread(lp_problem_t& leaf_pr continue; } + if (toc(stats_.start_time) > settings_.time_limit) { return; } + node_status_t node_status = solve_node(subtree, node_ptr, leaf_problem, Arow, upper_bound, log, 'D'); if (node_status == node_status_t::TIME_LIMIT) { - break; + return; } else if (node_status == node_status_t::HAS_CHILDREN) { auto [first, second] = child_selection(node_ptr); @@ -1035,7 +1038,6 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut original_lp_, stats_.start_time, lp_settings, root_relax_soln_, root_vstatus_, edge_norms_); stats_.total_lp_iters = root_relax_soln_.iterations; stats_.total_lp_solve_time = toc(stats_.start_time); - assert(root_vstatus_.size() == original_lp_.num_cols); if (root_status == lp_status_t::INFEASIBLE) { settings_.log.printf("MIP Infeasible\n"); // FIXME: rarely dual simplex detects infeasible whereas it is feasible. @@ -1059,6 +1061,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut return set_final_solution(solution, -inf); } + assert(root_vstatus_.size() == original_lp_.num_cols); set_uninitialized_steepest_edge_norms(edge_norms_); root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); diff --git a/cpp/src/dual_simplex/device_sparse_matrix.cuh b/cpp/src/dual_simplex/device_sparse_matrix.cuh index f347f956b..8113db973 100644 --- a/cpp/src/dual_simplex/device_sparse_matrix.cuh +++ b/cpp/src/dual_simplex/device_sparse_matrix.cuh @@ -173,18 +173,20 @@ class device_csc_matrix_t { col_index.resize(x.size(), stream); RAFT_CUDA_TRY(cudaMemsetAsync(col_index.data(), 0, sizeof(i_t) * col_index.size(), stream)); // Scatter 1 when there is a col start in col_index - thrust::for_each( - rmm::exec_policy(stream), - thrust::make_counting_iterator(i_t(1)), // Skip the first 0 - thrust::make_counting_iterator(static_cast(col_start.size() - 1)), // Skip the end index - [span_col_start = cuopt::make_span(col_start), - span_col_index = cuopt::make_span(col_index)] __device__(i_t i) { - span_col_index[span_col_start[i]] = 1; - }); + if (col_start.size() > 2) { + thrust::for_each(rmm::exec_policy(stream), + thrust::make_counting_iterator(i_t(1)), // Skip the first 0 + thrust::make_counting_iterator( + static_cast(col_start.size() - 1)), // Skip the end index + [span_col_start = cuopt::make_span(col_start), + span_col_index = cuopt::make_span(col_index)] __device__(i_t i) { + span_col_index[span_col_start[i]] = 1; + }); + } // Inclusive cumulative sum to have the corresponding column for each entry rmm::device_buffer d_temp_storage; - size_t temp_storage_bytes; + size_t temp_storage_bytes{0}; cub::DeviceScan::InclusiveSum( nullptr, temp_storage_bytes, col_index.data(), col_index.data(), col_index.size(), stream); d_temp_storage.resize(temp_storage_bytes, stream); diff --git a/cpp/src/dual_simplex/pinned_host_allocator.cu b/cpp/src/dual_simplex/pinned_host_allocator.cu index 8821930d3..86eafc2b2 100644 --- a/cpp/src/dual_simplex/pinned_host_allocator.cu +++ b/cpp/src/dual_simplex/pinned_host_allocator.cu @@ -57,6 +57,11 @@ bool operator!=(const PinnedHostAllocator&, const PinnedHostAllocator&) no template class PinnedHostAllocator; template double vector_norm_inf>( const std::vector>& x); + +template bool operator==(const PinnedHostAllocator&, + const PinnedHostAllocator&) noexcept; +template bool operator!=(const PinnedHostAllocator&, + const PinnedHostAllocator&) noexcept; #endif template class PinnedHostAllocator; diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 089b5cb9a..22ec49986 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -821,7 +821,9 @@ optimization_problem_solution_t solve_lp(optimization_problem_t>(); auto [reduced_problem, feasible] = presolver->apply(op_problem, diff --git a/cpp/src/mip/diversity/population.cu b/cpp/src/mip/diversity/population.cu index 94e8215ef..ae02ff738 100644 --- a/cpp/src/mip/diversity/population.cu +++ b/cpp/src/mip/diversity/population.cu @@ -211,7 +211,7 @@ std::vector> population_t::get_external_solutions sol.copy_new_assignment(h_entry.solution); sol.compute_feasibility(); if (!sol.get_feasible()) { - CUOPT_LOG_ERROR( + CUOPT_LOG_DEBUG( "External solution %d is infeasible, excess %g, obj %g, int viol %g, var viol %g, cstr " "viol %g, n_feasible %d/%d, integers %d/%d", counter, diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 4b69330d8..ee055b974 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -225,8 +225,13 @@ void local_search_t::stop_cpufj_scratch_threads() template bool local_search_t::do_fj_solve(solution_t& solution, fj_t& in_fj, + f_t time_limit, const std::string& source) { + if (time_limit == 0.) return solution.get_feasible(); + + timer_t timer(time_limit); + auto h_weights = cuopt::host_copy(in_fj.cstr_weights, solution.handle_ptr->get_stream()); auto h_objective_weight = in_fj.objective_weight.value(solution.handle_ptr->get_stream()); for (auto& cpu_fj : ls_cpu_fj) { @@ -242,7 +247,8 @@ bool local_search_t::do_fj_solve(solution_t& solution, } // Run GPU solver and measure execution time - auto gpu_fj_start = std::chrono::high_resolution_clock::now(); + auto gpu_fj_start = std::chrono::high_resolution_clock::now(); + in_fj.settings.time_limit = timer.remaining_time(); in_fj.solve(solution); // Stop CPU solver @@ -320,9 +326,9 @@ void local_search_t::generate_fast_solution(solution_t& solu constraint_prop.apply_round(solution, 1., constr_prop_timer); if (solution.compute_feasibility()) { return; } if (timer.check_time_limit()) { return; }; - fj.settings.time_limit = std::min(3., timer.remaining_time()); + f_t time_limit = std::min(3., timer.remaining_time()); // run fj on the solution - do_fj_solve(solution, fj, "fast"); + do_fj_solve(solution, fj, time_limit, "fast"); // TODO check if FJ returns the same solution // check if the solution is feasible if (solution.compute_feasibility()) { return; } @@ -381,11 +387,11 @@ bool local_search_t::run_fj_until_timer(solution_t& solution bool is_feasible; fj.settings.n_of_minimums_for_exit = 1e6; fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; - fj.settings.time_limit = timer.remaining_time() * 0.95; fj.settings.update_weights = false; fj.settings.feasibility_run = false; fj.copy_weights(weights, solution.handle_ptr); - do_fj_solve(solution, fj, "until_timer"); + f_t time_limit = timer.remaining_time() * 0.95; + do_fj_solve(solution, fj, time_limit, "until_timer"); CUOPT_LOG_DEBUG("Initial FJ feasibility done"); is_feasible = solution.compute_feasibility(); if (fj.settings.feasibility_run || timer.check_time_limit()) { return is_feasible; } @@ -410,11 +416,11 @@ bool local_search_t::run_fj_annealing(solution_t& solution, fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; fj.settings.candidate_selection = fj_candidate_selection_t::FEASIBLE_FIRST; fj.settings.iteration_limit = ls_config.iteration_limit; - fj.settings.time_limit = std::min(10., timer.remaining_time()); fj.settings.parameters.allow_infeasibility_iterations = 100; fj.settings.update_weights = 1; fj.settings.baseline_objective_for_longer_run = ls_config.best_objective_of_parents; - do_fj_solve(solution, fj, "annealing"); + f_t time_limit = std::min(10., timer.remaining_time()); + do_fj_solve(solution, fj, time_limit, "annealing"); bool is_feasible = solution.compute_feasibility(); fj.settings = prev_settings; @@ -460,14 +466,14 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu solution.assign_random_within_bounds(perturbation_ratio); } cuopt_func_call(solution.test_variable_bounds(false)); - f_t lp_run_time_after_feasible = 1.; + f_t lp_run_time_after_feasible = std::min(1., timer.remaining_time()); timer_t bounds_prop_timer = timer_t(std::min(timer.remaining_time(), 10.)); bool is_feasible = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); if (!is_feasible) { const f_t lp_run_time = 2.; relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_run_time; + lp_settings.time_limit = std::min(lp_run_time, timer.remaining_time()); lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; run_lp_with_vars_fixed( *solution.problem_ptr, solution, solution.problem_ptr->integer_indices, lp_settings); @@ -479,8 +485,8 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu fj.settings.n_of_minimums_for_exit = 20000; fj.settings.update_weights = true; fj.settings.feasibility_run = false; - fj.settings.time_limit = std::min(30., timer.remaining_time()); - do_fj_solve(solution, fj, "on_lp_optimal"); + f_t time_limit = std::min(30., timer.remaining_time()); + do_fj_solve(solution, fj, time_limit, "on_lp_optimal"); return solution.get_feasible(); } @@ -496,8 +502,8 @@ bool local_search_t::run_fj_on_zero(solution_t& solution, ti fj.settings.n_of_minimums_for_exit = 20000; fj.settings.update_weights = true; fj.settings.feasibility_run = false; - fj.settings.time_limit = std::min(30., timer.remaining_time()); - bool is_feasible = do_fj_solve(solution, fj, "on_zero"); + f_t time_limit = std::min(30., timer.remaining_time()); + bool is_feasible = do_fj_solve(solution, fj, time_limit, "on_zero"); return is_feasible; } diff --git a/cpp/src/mip/local_search/local_search.cuh b/cpp/src/mip/local_search/local_search.cuh index e8dc016e2..1219a7d3f 100644 --- a/cpp/src/mip/local_search/local_search.cuh +++ b/cpp/src/mip/local_search/local_search.cuh @@ -113,7 +113,10 @@ class local_search_t { population_t* population_ptr = nullptr); void resize_vectors(problem_t& problem, const raft::handle_t* handle_ptr); - bool do_fj_solve(solution_t& solution, fj_t& fj, const std::string& source); + bool do_fj_solve(solution_t& solution, + fj_t& fj, + f_t time_limit, + const std::string& source); i_t ls_threads() const { return ls_cpu_fj.size() + scratch_cpu_fj.size(); } void save_solution_and_add_cutting_plane(solution_t& solution,