From 8b5cdfd5ec51e44ed51116d80a6d4a775810bc01 Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Tue, 2 Sep 2025 15:09:52 -0400 Subject: [PATCH 01/10] in progress. removing variable_lower/upper bounds --- cpp/src/linear_programming/pdlp.cu | 43 ++- .../restart_strategy/pdlp_restart_strategy.cu | 49 ++- .../convergence_information.cu | 42 ++- .../infeasibility_information.cu | 57 ++-- cpp/src/linear_programming/utils.cuh | 50 ++- .../feasibility_pump/feasibility_pump.cu | 41 ++- .../local_search/rounding/constraint_prop.cu | 287 ++++++++++-------- .../load_balanced_partition_helpers.cuh | 39 --- cpp/src/mip/problem/host_helper.cuh | 4 + cpp/src/mip/problem/problem.cu | 131 +++++--- cpp/src/mip/problem/problem.cuh | 23 +- cpp/src/mip/problem/problem_helpers.cuh | 50 +++ cpp/src/utilities/copy_helpers.hpp | 40 +++ 13 files changed, 545 insertions(+), 311 deletions(-) diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index 84b04d43f9..c7cc09067b 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -1041,20 +1041,35 @@ optimization_problem_solution_t pdlp_solver_t::run_solver( // Project initial primal solution if (pdlp_hyper_params::project_initial_primal) { - raft::linalg::ternaryOp(pdhg_solver_.get_primal_solution().data(), - pdhg_solver_.get_primal_solution().data(), - op_problem_scaled_.variable_lower_bounds.data(), - op_problem_scaled_.variable_upper_bounds.data(), - primal_size_h_, - clamp(), - stream_view_); - raft::linalg::ternaryOp(unscaled_primal_avg_solution_.data(), - unscaled_primal_avg_solution_.data(), - op_problem_scaled_.variable_lower_bounds.data(), - op_problem_scaled_.variable_upper_bounds.data(), - primal_size_h_, - clamp(), - stream_view_); + // raft::linalg::ternaryOp(pdhg_solver_.get_primal_solution().data(), + // pdhg_solver_.get_primal_solution().data(), + // op_problem_scaled_.variable_lower_bounds.data(), + // op_problem_scaled_.variable_upper_bounds.data(), + // primal_size_h_, + // clamp(), + // stream_view_); + // raft::linalg::ternaryOp(unscaled_primal_avg_solution_.data(), + // unscaled_primal_avg_solution_.data(), + // op_problem_scaled_.variable_lower_bounds.data(), + // op_problem_scaled_.variable_upper_bounds.data(), + // primal_size_h_, + // clamp(), + // stream_view_); + using f_t2 = typename type_2::type; + cub::DeviceTransform::Transform( + cuda::std::make_tuple(pdhg_solver_.get_primal_solution().data(), + op_problem_scaled_.variable_bounds.data()), + pdhg_solver_.get_primal_solution().data(), + primal_size_h_, + clamp(), + stream_view_); + cub::DeviceTransform::Transform( + cuda::std::make_tuple(unscaled_primal_avg_solution_.data(), + op_problem_scaled_.variable_bounds.data()), + unscaled_primal_avg_solution_.data(), + primal_size_h_, + clamp(), + stream_view_); } if (verbose) { diff --git a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu index 236ec373d8..de2e37584a 100644 --- a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu +++ b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu @@ -1388,6 +1388,14 @@ median breakpoint is evaluated, eliminating half of the components. The process is iterated until the argmin is identified. */ +template +struct extract_bounds_t { + __device__ thrust::tuple operator()(f_t2 bounds) + { + return thrust::make_tuple(bounds.x, bounds.y); + } +}; + template void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( localized_duality_gap_container_t& duality_gap, @@ -1451,10 +1459,20 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( // component becomes fixed by its bounds // Copying primal / dual bound before sorting them according to threshold - raft::copy( - lower_bound_.data(), problem_ptr->variable_lower_bounds.data(), primal_size_h_, stream_view_); - raft::copy( - upper_bound_.data(), problem_ptr->variable_upper_bounds.data(), primal_size_h_, stream_view_); + // TODO : transform + // raft::copy( + // lower_bound_.data(), problem_ptr->variable_lower_bounds.data(), primal_size_h_, + // stream_view_); + // raft::copy( + // upper_bound_.data(), problem_ptr->variable_upper_bounds.data(), primal_size_h_, + // stream_view_); + using f_t2 = typename type_2::type; + cub::DeviceTransform::Transform( + problem_ptr->variable_bounds.data(), + thrust::make_zip_iterator(thrust::make_tuple(lower_bound_.data(), upper_bound_.data())), + primal_size_h_, + extract_bounds_t(), + stream_view_); raft::copy(lower_bound_.data() + primal_size_h_, transformed_constraint_lower_bounds_.data(), dual_size_h_, @@ -1632,13 +1650,20 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( a_add_scalar_times_b(target_threshold_.data()), stream_view_); // project by max(min(x[i], upperbound[i]),lowerbound[i]) for primal part - raft::linalg::ternaryOp(duality_gap.primal_solution_tr_.data(), - duality_gap.primal_solution_tr_.data(), - problem_ptr->variable_lower_bounds.data(), - problem_ptr->variable_upper_bounds.data(), - primal_size_h_, - clamp(), - stream_view_); + // raft::linalg::ternaryOp(duality_gap.primal_solution_tr_.data(), + // duality_gap.primal_solution_tr_.data(), + // problem_ptr->variable_lower_bounds.data(), + // problem_ptr->variable_upper_bounds.data(), + // primal_size_h_, + // clamp(), + // stream_view_); + using f_t2 = typename type_2::type; + cub::DeviceTransform::Transform(cuda::std::make_tuple(duality_gap.primal_solution_tr_.data(), + problem_ptr->variable_bounds.data()), + duality_gap.primal_solution_tr_.data(), + primal_size_h_, + clamp(), + stream_view_); // project by max(min(y[i], upperbound[i]),lowerbound[i]) raft::linalg::ternaryOp(duality_gap.dual_solution_tr_.data(), @@ -1646,7 +1671,7 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( transformed_constraint_lower_bounds_.data(), transformed_constraint_upper_bounds_.data(), dual_size_h_, - clamp(), + constraint_clamp(), stream_view_); // } } diff --git a/cpp/src/linear_programming/termination_strategy/convergence_information.cu b/cpp/src/linear_programming/termination_strategy/convergence_information.cu index 2378b8b9bf..e69c8abd98 100644 --- a/cpp/src/linear_programming/termination_strategy/convergence_information.cu +++ b/cpp/src/linear_programming/termination_strategy/convergence_information.cu @@ -336,7 +336,7 @@ void convergence_information_t::compute_dual_objective( problem_ptr->constraint_lower_bounds.data(), problem_ptr->constraint_upper_bounds.data(), dual_size_h_, - bound_value_reduced_cost_product(), + constraint_bound_value_reduced_cost_product(), stream_view_); cub::DeviceReduce::Sum(rmm_tmp_buffer_.data(), @@ -371,13 +371,19 @@ void convergence_information_t::compute_reduced_cost_from_primal_gradi { raft::common::nvtx::range fun_scope("compute_reduced_cost_from_primal_gradient"); - raft::linalg::ternaryOp(bound_value_.data(), - primal_gradient.data(), - problem_ptr->variable_lower_bounds.data(), - problem_ptr->variable_upper_bounds.data(), - primal_size_h_, - bound_value_gradient(), - stream_view_); + using f_t2 = typename type_2::type; + // raft::linalg::binaryOp(bound_value_.data(), + // primal_gradient.data(), + // problem_ptr->variable_bounds.data(), + // primal_size_h_, + // bound_value_gradient(), + // stream_view_); + cub::DeviceTransform::Transform( + cuda::std::make_tuple(primal_gradient.data(), problem_ptr->variable_bounds.data()), + bound_value_.data(), + primal_size_h_, + bound_value_gradient(), + stream_view_); if (pdlp_hyper_params::handle_some_primal_gradients_on_finite_bounds_as_residuals) { raft::linalg::ternaryOp(reduced_cost_.data(), @@ -402,15 +408,21 @@ void convergence_information_t::compute_reduced_costs_dual_objective_c { raft::common::nvtx::range fun_scope("compute_reduced_costs_dual_objective_contribution"); + using f_t2 = typename type_2::type; // if reduced cost is positive -> lower bound, negative -> upper bounds, 0 -> 0 // if bound_val is not finite let element be -inf, otherwise bound_value*reduced_cost - raft::linalg::ternaryOp(bound_value_.data(), - reduced_cost_.data(), - problem_ptr->variable_lower_bounds.data(), - problem_ptr->variable_upper_bounds.data(), - primal_size_h_, - bound_value_reduced_cost_product(), - stream_view_); + // raft::linalg::binaryOp(bound_value_.data(), + // reduced_cost_.data(), + // problem_ptr->variable_bounds.data(), + // primal_size_h_, + // bound_value_reduced_cost_product(), + // stream_view_); + cub::DeviceTransform::Transform( + cuda::std::make_tuple(reduced_cost_.data(), problem_ptr->variable_bounds.data()), + bound_value_.data(), + primal_size_h_, + bound_value_reduced_cost_product(), + stream_view_); // sum over bound_value*reduced_cost, but should be -inf if any element is -inf cub::DeviceReduce::Sum(rmm_tmp_buffer_.data(), diff --git a/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu b/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu index fd9651d9a2..e96c19e70d 100644 --- a/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu +++ b/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu @@ -254,16 +254,14 @@ void infeasibility_information_t::compute_max_violation( // Convert raw pointer to thrust::device_ptr to write directly device side through reduce thrust::device_ptr primal_ray_max_violation(primal_ray_max_violation_.data()); + using f_t2 = typename type_2::type; *primal_ray_max_violation = thrust::transform_reduce( handle_ptr_->get_thrust_policy(), - thrust::make_zip_iterator(thrust::make_tuple(primal_ray.data(), - problem_ptr->variable_lower_bounds.data(), - problem_ptr->variable_upper_bounds.data())), thrust::make_zip_iterator( - thrust::make_tuple(primal_ray.data() + primal_size_h_, - problem_ptr->variable_lower_bounds.data() + primal_size_h_, - problem_ptr->variable_upper_bounds.data() + primal_size_h_)), - max_violation(), + thrust::make_tuple(primal_ray.data(), problem_ptr->variable_bounds.data())), + thrust::make_zip_iterator(thrust::make_tuple( + primal_ray.data() + primal_size_h_, problem_ptr->variable_bounds.data() + primal_size_h_)), + max_violation(), f_t(0.0), thrust::maximum()); } @@ -329,7 +327,7 @@ void infeasibility_information_t::compute_homogenous_dual_objective( problem_ptr->constraint_lower_bounds.data(), problem_ptr->constraint_upper_bounds.data(), dual_size_h_, - bound_value_reduced_cost_product(), + constraint_bound_value_reduced_cost_product(), stream_view_); cub::DeviceReduce::Sum(rmm_tmp_buffer_.data(), @@ -364,13 +362,20 @@ template void infeasibility_information_t::compute_reduced_cost_from_primal_gradient( rmm::device_uvector& primal_gradient, rmm::device_uvector& primal_ray) { - raft::linalg::ternaryOp(bound_value_.data(), - primal_gradient.data(), - problem_ptr->variable_lower_bounds.data(), - problem_ptr->variable_upper_bounds.data(), - primal_size_h_, - bound_value_gradient(), - stream_view_); + using f_t2 = typename type_2::type; + // raft::linalg::binaryOp(bound_value_.data(), + // primal_gradient.data(), + // problem_ptr->variable_bounds.data(), + // //problem_ptr->variable_upper_bounds.data(), + // primal_size_h_, + // bound_value_gradient(), + // stream_view_); + cub::DeviceTransform::Transform( + cuda::std::make_tuple(primal_gradient.data(), problem_ptr->variable_bounds.data()), + bound_value_.data(), + primal_size_h_, + bound_value_gradient(), + stream_view_); if (pdlp_hyper_params::handle_some_primal_gradients_on_finite_bounds_as_residuals) { raft::linalg::ternaryOp(reduced_cost_.data(), @@ -393,16 +398,24 @@ void infeasibility_information_t::compute_reduced_cost_from_primal_gra template void infeasibility_information_t::compute_reduced_costs_dual_objective_contribution() { + using f_t2 = typename type_2::type; // Check if these bounds are the same as computed above // if reduced cost is positive -> lower bound, negative -> upper bounds, 0 -> 0 // if bound_val is not finite let element be -inf, otherwise bound_value*reduced_cost - raft::linalg::ternaryOp(bound_value_.data(), - reduced_cost_.data(), - problem_ptr->variable_lower_bounds.data(), - problem_ptr->variable_upper_bounds.data(), - primal_size_h_, - bound_value_reduced_cost_product(), - stream_view_); + // raft::linalg::binaryOp(bound_value_.data(), + // reduced_cost_.data(), + // //problem_ptr->variable_lower_bounds.data(), + // //problem_ptr->variable_upper_bounds.data(), + // problem_ptr->variable_bounds.data(), + // primal_size_h_, + // bound_value_reduced_cost_product(), + // stream_view_); + cub::DeviceTransform::Transform( + cuda::std::make_tuple(reduced_cost_.data(), problem_ptr->variable_bounds.data()), + bound_value_.data(), + primal_size_h_, + bound_value_reduced_cost_product(), + stream_view_); // sum over bound_value*reduced_cost cub::DeviceReduce::Sum(rmm_tmp_buffer_.data(), diff --git a/cpp/src/linear_programming/utils.cuh b/cpp/src/linear_programming/utils.cuh index d4df2815b1..bf685ef6df 100644 --- a/cpp/src/linear_programming/utils.cuh +++ b/cpp/src/linear_programming/utils.cuh @@ -129,13 +129,21 @@ struct a_divides_sqrt_b_bounded { }; template -struct clamp { +struct constraint_clamp { __device__ f_t operator()(f_t value, f_t lower, f_t upper) { return raft::min(raft::max(value, lower), upper); } }; +template +struct clamp { + __device__ f_t operator()(f_t value, f_t2 bounds) + { + return raft::min(raft::max(value, bounds.x), bounds.y); + } +}; + template struct combine_finite_abs_bounds { __device__ __host__ f_t operator()(f_t lower, f_t upper) @@ -177,32 +185,35 @@ struct violation { } }; -template +template struct max_violation { max_violation() {} - __device__ f_t operator()(const thrust::tuple& t) const + __device__ f_t operator()(const thrust::tuple& t) const { - const f_t value = thrust::get<0>(t); - const f_t lower = thrust::get<1>(t); - const f_t upper = thrust::get<2>(t); - f_t local_max = f_t(0.0); + const f_t value = thrust::get<0>(t); + const f_t2 bounds = thrust::get<1>(t); + const f_t lower = bounds.x; + const f_t upper = bounds.y; + f_t local_max = f_t(0.0); if (isfinite(lower)) { local_max = raft::max(local_max, -value); } if (isfinite(upper)) { local_max = raft::max(local_max, value); } return local_max; } }; -template +template struct bound_value_gradient { - __device__ f_t operator()(f_t value, f_t lower, f_t upper) + __device__ f_t operator()(f_t value, f_t2 bounds) { + f_t lower = bounds.x; + f_t upper = bounds.y; if (value > f_t(0) && value < f_t(0)) { return 0; } return value > f_t(0) ? lower : upper; } }; template -struct bound_value_reduced_cost_product { +struct constraint_bound_value_reduced_cost_product { __device__ f_t operator()(f_t value, f_t lower, f_t upper) { f_t bound_value = f_t(0); @@ -218,6 +229,25 @@ struct bound_value_reduced_cost_product { } }; +template +struct bound_value_reduced_cost_product { + __device__ f_t operator()(f_t value, f_t2 variable_bounds) + { + f_t lower = variable_bounds.x; + f_t upper = variable_bounds.y; + f_t bound_value = f_t(0); + if (value > f_t(0)) { + // A positive reduced cost is associated with a binding lower bound. + bound_value = lower; + } else if (value < f_t(0)) { + // A negative reduced cost is associated with a binding upper bound. + bound_value = upper; + } + f_t val = isfinite(bound_value) ? value * bound_value : f_t(0); + return val; + } +}; + template struct copy_gradient_if_should_be_reduced_cost { __device__ f_t operator()(f_t value, f_t bound, f_t gradient) diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index ee04155d6c..5a7c157133 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -147,11 +147,9 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tvariable_upper_bounds, - solution.handle_ptr->get_stream()); - auto h_variable_lower_bounds = cuopt::host_copy(solution.problem_ptr->variable_lower_bounds, - solution.handle_ptr->get_stream()); + auto h_assignment = solution.get_host_assignment(); + auto h_variable_bounds = + cuopt::host_copy(solution.problem_ptr->variable_bounds, solution.handle_ptr->get_stream()); auto h_last_projection = cuopt::host_copy(last_projection, solution.handle_ptr->get_stream()); const f_t int_tol = context.settings.tolerances.integrality_tolerance; constraints_delta_t h_constraints; @@ -164,12 +162,13 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tinteger_equal(h_assignment[i], h_variable_upper_bounds[i])) { - obj_offset += h_variable_upper_bounds[i]; + auto h_var_bounds = h_variable_bounds[i]; + if (solution.problem_ptr->integer_equal(h_assignment[i], h_var_bounds.y)) { + obj_offset += h_var_bounds.y; // set the objective weight to -1, u - x obj_coefficients[i] = -1; - } else if (solution.problem_ptr->integer_equal(h_assignment[i], h_variable_lower_bounds[i])) { - obj_offset -= h_variable_lower_bounds[i]; + } else if (solution.problem_ptr->integer_equal(h_assignment[i], h_var_bounds.x)) { + obj_offset -= h_var_bounds.x; // set the objective weight to +1, x - l obj_coefficients[i] = 1; } else { @@ -177,10 +176,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t::relax_general_integers(solution_t& { orig_variable_types.resize(solution.problem_ptr->n_variables, solution.handle_ptr->get_stream()); - auto var_types = make_span(solution.problem_ptr->variable_types); - auto var_lb = make_span(solution.problem_ptr->variable_lower_bounds); - auto var_ub = make_span(solution.problem_ptr->variable_upper_bounds); + auto var_types = make_span(solution.problem_ptr->variable_types); + // auto var_lb = make_span(solution.problem_ptr->variable_lower_bounds); + // auto var_ub = make_span(solution.problem_ptr->variable_upper_bounds); + auto var_bnds = make_span(solution.problem_ptr->variable_bounds); auto copy_types = make_span(orig_variable_types); raft::copy(orig_variable_types.data(), @@ -454,12 +451,14 @@ void feasibility_pump_t::relax_general_integers(solution_t& solution.handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(solution.problem_ptr->n_variables), - [var_types, var_lb, var_ub, copy_types, pb = solution.problem_ptr->view()] __device__( - auto v_idx) { + [var_types, var_bnds, copy_types, pb = solution.problem_ptr->view()] __device__(auto v_idx) { auto orig_v_type = var_types[v_idx]; - auto lb = var_lb[v_idx]; - auto ub = var_ub[v_idx]; - bool var_binary = (pb.integer_equal(lb, 0) && pb.integer_equal(ub, 1)); + // auto lb = var_lb[v_idx]; + // auto ub = var_ub[v_idx]; + auto var_bounds = var_bnds[v_idx]; + auto lb = var_bounds.x; + auto ub = var_bounds.y; + bool var_binary = (pb.integer_equal(lb, 0) && pb.integer_equal(ub, 1)); auto copy_type = (orig_v_type == var_t::INTEGER) && var_binary ? var_t::INTEGER : var_t::CONTINUOUS; var_types[v_idx] = copy_type; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index e557d3ba81..6eb7daad73 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -130,8 +130,11 @@ __global__ void compute_implied_slack_consumption_per_var( i_t var_offset = pb.reverse_offsets[var_idx]; i_t var_degree = pb.reverse_offsets[var_idx + 1] - var_offset; f_t th_var_implied_slack_consumption = 0.; - f_t lb = pb.variable_lower_bounds[var_idx]; - f_t ub = pb.variable_upper_bounds[var_idx]; + // f_t lb = pb.variable_lower_bounds[var_idx]; + // f_t ub = pb.variable_upper_bounds[var_idx]; + auto var_bnd = pb.variable_bounds[var_idx]; + f_t lb = var_bnd.x; + f_t ub = var_bnd.y; for (i_t i = threadIdx.x; i < var_degree; i += blockDim.x) { auto a = pb.reverse_coefficients[var_offset + i]; auto cnst_idx = pb.reverse_constraints[var_offset + i]; @@ -206,25 +209,26 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t // we can't call this function when the problem is ii. it causes false offset computations // TODO add assert that the problem is not ii auto assgn = make_span(sol.assignment); - thrust::stable_sort(sol.handle_ptr->get_thrust_policy(), - vars.begin(), - vars.end(), - [lb = sol.problem_ptr->variable_lower_bounds.data(), - ub = sol.problem_ptr->variable_upper_bounds.data(), - assgn] __device__(i_t v_idx_1, i_t v_idx_2) { - f_t bounds_interval_1 = ub[v_idx_1] - lb[v_idx_1]; - f_t bounds_interval_2 = ub[v_idx_2] - lb[v_idx_2]; - // if bounds interval are equal (binary and ternary) check fraction - // if both bounds intervals are greater than 2. then do fraction - if ((bounds_interval_1 == bounds_interval_2) || - (bounds_interval_1 > 2 && bounds_interval_2 > 2)) { - f_t frac_1 = get_fractionality_of_val(assgn[v_idx_1]); - f_t frac_2 = get_fractionality_of_val(assgn[v_idx_2]); - return frac_1 < frac_2; - } else { - return bounds_interval_1 < bounds_interval_2; - } - }); + thrust::stable_sort( + sol.handle_ptr->get_thrust_policy(), + vars.begin(), + vars.end(), + [bnds = sol.problem_ptr->variable_bounds.data(), assgn] __device__(i_t v_idx_1, i_t v_idx_2) { + auto bnd_1 = bnds[v_idx_1]; + auto bnd_2 = bnds[v_idx_2]; + f_t bounds_interval_1 = bnd_1.y - bnd_1.x; + f_t bounds_interval_2 = bnd_2.y - bnd_2.x; + // if bounds interval are equal (binary and ternary) check fraction + // if both bounds intervals are greater than 2. then do fraction + if ((bounds_interval_1 == bounds_interval_2) || + (bounds_interval_1 > 2 && bounds_interval_2 > 2)) { + f_t frac_1 = get_fractionality_of_val(assgn[v_idx_1]); + f_t frac_2 = get_fractionality_of_val(assgn[v_idx_2]); + return frac_1 < frac_2; + } else { + return bounds_interval_1 < bounds_interval_2; + } + }); // now do the suffling, for that we need to assign some random values to rnd array // we will sort this rnd array and the vars in subsections, so that each subsection will be // shuffled in total we will have 3(binary, ternary and rest) x 7 intervals = 21 subsections. @@ -237,15 +241,16 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t thrust::for_each(sol.handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator((i_t)vars.size() - 1), - [lb = make_span(sol.problem_ptr->variable_lower_bounds), - ub = make_span(sol.problem_ptr->variable_upper_bounds), + [bnds = make_span(sol.problem_ptr->variable_bounds), offsets = make_span(subsection_offsets), vars, assgn] __device__(i_t idx) { i_t var_1 = vars[idx]; i_t var_2 = vars[idx + 1]; - f_t bounds_interval_1 = ub[var_1] - lb[var_1]; - f_t bounds_interval_2 = ub[var_2] - lb[var_2]; + auto bnd_1 = bnds[var_1]; + auto bnd_2 = bnds[var_2]; + f_t bounds_interval_1 = bnd_1.y - bnd_1.x; + f_t bounds_interval_2 = bnd_2.y - bnd_2.x; f_t frac_1 = get_fractionality_of_val(assgn[var_1]); f_t frac_2 = get_fractionality_of_val(assgn[var_2]); if (bounds_interval_1 == 1 && bounds_interval_2 == 1) { @@ -390,24 +395,27 @@ void constraint_prop_t::collapse_crossing_bounds(problem_t& problem_t& orig_problem, const raft::handle_t* handle_ptr) { - auto lb = make_span(problem.variable_lower_bounds); - auto ub = make_span(problem.variable_upper_bounds); - auto original_lb = make_span(orig_problem.variable_lower_bounds); - auto original_ub = make_span(orig_problem.variable_upper_bounds); + // auto lb = make_span(problem.variable_lower_bounds); + // auto ub = make_span(problem.variable_upper_bounds); + // auto original_lb = make_span(orig_problem.variable_lower_bounds); + // auto original_ub = make_span(orig_problem.variable_upper_bounds); + + auto v_bnds = make_span(problem.variable_bounds); + auto original_v_bnds = make_span(orig_problem.variable_bounds); thrust::for_each( handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), - thrust::make_counting_iterator((i_t)lb.size()), - [lb, - ub, - original_lb, - original_ub, + thrust::make_counting_iterator((i_t)v_bnds.size()), + [v_bnds, + original_v_bnds, variable_types = make_span(problem.variable_types), int_tol = problem.tolerances.integrality_tolerance] __device__(i_t idx) { - auto v_lb = lb[idx]; - auto v_ub = ub[idx]; - auto o_lb = original_lb[idx]; - auto o_ub = original_ub[idx]; + auto v_bnd = v_bnds[idx]; + auto ov_bnd = original_v_bnds[idx]; + auto v_lb = v_bnd.x; + auto v_ub = v_bnd.y; + auto o_lb = ov_bnd.x; + auto o_ub = ov_bnd.y; if (v_lb > v_ub) { f_t val_to_collapse; if (variable_types[idx] == var_t::INTEGER) { @@ -422,8 +430,8 @@ void constraint_prop_t::collapse_crossing_bounds(problem_t& cuopt_assert(o_lb - int_tol <= val_to_collapse && val_to_collapse <= o_ub + int_tol, "Out of original bounds!"); - lb[idx] = val_to_collapse; - ub[idx] = val_to_collapse; + using f_t2 = typename type_2::type; + v_bnds[idx] = f_t2{val_to_collapse, val_to_collapse}; } }); } @@ -432,50 +440,46 @@ template void constraint_prop_t::set_bounds_on_fixed_vars(solution_t& sol) { auto assgn = make_span(sol.assignment); - auto lb = make_span(sol.problem_ptr->variable_lower_bounds); - auto ub = make_span(sol.problem_ptr->variable_upper_bounds); + // auto lb = make_span(sol.problem_ptr->variable_lower_bounds); + // auto ub = make_span(sol.problem_ptr->variable_upper_bounds); + auto var_bounds = make_span(sol.problem_ptr->variable_bounds); thrust::for_each(sol.handle_ptr->get_thrust_policy(), sol.problem_ptr->integer_indices.begin(), sol.problem_ptr->integer_indices.end(), - [pb = sol.problem_ptr->view(), assgn, lb, ub] __device__(i_t idx) { + [pb = sol.problem_ptr->view(), assgn, var_bounds] __device__(i_t idx) { + using f_t2 = typename type_2::type; auto var_val = assgn[idx]; if (pb.is_integer(var_val)) { - lb[idx] = var_val; - ub[idx] = var_val; + var_bounds[idx] = f_t2{var_val, var_val}; + // lb[idx] = var_val; + // ub[idx] = var_val; } }); } -template +template struct is_bound_fixed_t { // This functor should be called only on integer variables f_t eps; - raft::device_span lb; - raft::device_span ub; - raft::device_span original_lb; - raft::device_span original_ub; + raft::device_span bnd; + raft::device_span original_bnd; raft::device_span assignment; is_bound_fixed_t(f_t eps_, - raft::device_span lb_, - raft::device_span ub_, - raft::device_span original_lb_, - raft::device_span original_ub_, + raft::device_span bnd_, + raft::device_span original_bnd_, raft::device_span assignment_) - : eps(eps_), - lb(lb_), - ub(ub_), - original_lb(original_lb_), - original_ub(original_ub_), - assignment(assignment_) + : eps(eps_), bnd(bnd_), original_bnd(original_bnd_), assignment(assignment_) { } HDI bool operator()(i_t idx) { - auto v_lb = lb[idx]; - auto v_ub = ub[idx]; - auto o_lb = original_lb[idx]; - auto o_ub = original_ub[idx]; + auto v_bnd = bnd[idx]; + auto v_lb = v_bnd.x; + auto v_ub = v_bnd.y; + auto ov_bnd = bnd[idx]; + auto o_lb = ov_bnd.x; + auto o_ub = ov_bnd.y; bool is_singleton = round_val_on_singleton_and_crossing(assignment[idx], v_lb, v_ub, o_lb, o_ub); return is_singleton; @@ -636,6 +640,18 @@ thrust::pair constraint_prop_t::generate_double_probing_pair return thrust::make_pair(first_probe, second_probe); } +template +bool test_var_out_of_bounds(const solution_t& orig_sol, + i_t unset_var_idx, + f_t probe, + f_t int_tol, + const raft::handle_t* handle_ptr) +{ + auto var_bnd = + orig_sol.problem_ptr->variable_bounds.element(unset_var_idx, handle_ptr->get_stream()); + return (var_bnd.x <= probe + int_tol) && (probe - int_tol <= var_bnd.y); +} + template std::tuple, std::vector, std::vector> constraint_prop_t::generate_bulk_rounding_vector( @@ -661,16 +677,22 @@ constraint_prop_t::generate_bulk_rounding_vector( cuda::std::tie(first_probe, second_probe) = generate_double_probing_pair(sol, orig_sol, unset_var_idx, probing_config, false); } - cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( - unset_var_idx, sol.handle_ptr->get_stream()) <= first_probe + int_tol && - first_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( - unset_var_idx, sol.handle_ptr->get_stream()), + cuopt_assert(test_var_out_of_bounds( + orig_sol, unset_var_idx, first_probe, int_tol, sol.handle_ptr->get_stream()), "Variable out of original bounds!"); - cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( - unset_var_idx, sol.handle_ptr->get_stream()) <= second_probe + int_tol && - second_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( - unset_var_idx, sol.handle_ptr->get_stream()), + cuopt_assert(test_var_out_of_bounds( + orig_sol, unset_var_idx, second_probe, int_tol, sol.handle_ptr->get_stream()), "Variable out of original bounds!"); + // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( + // unset_var_idx, sol.handle_ptr->get_stream()) <= first_probe + int_tol && + // first_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( + // unset_var_idx, sol.handle_ptr->get_stream()), + // "Variable out of original bounds!"); + // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( + // unset_var_idx, sol.handle_ptr->get_stream()) <= second_probe + int_tol && + // second_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( + // unset_var_idx, sol.handle_ptr->get_stream()), + // "Variable out of original bounds!"); cuopt_assert(orig_sol.problem_ptr->is_integer(first_probe), "Probing value must be an integer"); cuopt_assert(orig_sol.problem_ptr->is_integer(second_probe), "Probing value must be an integer"); @@ -688,16 +710,22 @@ constraint_prop_t::generate_bulk_rounding_vector( int_tol); if (val_to_round == second_probe) { second_probe = first_probe; } } - cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( - unset_var_idx, sol.handle_ptr->get_stream()) <= val_to_round + int_tol && - val_to_round - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( - unset_var_idx, sol.handle_ptr->get_stream()), + cuopt_assert(test_var_out_of_bounds( + orig_sol, unset_var_idx, val_to_round, int_tol, sol.handle_ptr->get_stream()), "Variable out of original bounds!"); - cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( - unset_var_idx, sol.handle_ptr->get_stream()) <= second_probe + int_tol && - second_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( - unset_var_idx, sol.handle_ptr->get_stream()), + cuopt_assert(test_var_out_of_bounds( + orig_sol, unset_var_idx, second_probe, int_tol, sol.handle_ptr->get_stream()), "Variable out of original bounds!"); + // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( + // unset_var_idx, sol.handle_ptr->get_stream()) <= val_to_round + int_tol && + // val_to_round - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( + // unset_var_idx, sol.handle_ptr->get_stream()), + // "Variable out of original bounds!"); + // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( + // unset_var_idx, sol.handle_ptr->get_stream()) <= second_probe + int_tol && + // second_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( + // unset_var_idx, sol.handle_ptr->get_stream()), + // "Variable out of original bounds!"); std::get<0>(var_probe_vals)[i] = unset_var_idx; std::get<1>(var_probe_vals)[i] = val_to_round; std::get<2>(var_probe_vals)[i] = second_probe; @@ -716,21 +744,39 @@ void constraint_prop_t::update_host_assignment(const solution_tget_stream()); } +template +struct extract_bounds_t { + __device__ thrust::tuple operator()(f_t2 bounds) + { + return thrust::make_tuple(bounds.x, bounds.y); + } +}; + template void constraint_prop_t::set_host_bounds(const solution_t& sol) { - cuopt_assert(sol.problem_ptr->variable_lower_bounds.size() == multi_probe.host_lb.size(), + // cuopt_assert(sol.problem_ptr->variable_lower_bounds.size() == multi_probe.host_lb.size(), + // "size of variable lower bound mismatch"); + // raft::copy(multi_probe.host_lb.data(), + // sol.problem_ptr->variable_lower_bounds.data(), + // sol.problem_ptr->variable_lower_bounds.size(), + // sol.handle_ptr->get_stream()); + // cuopt_assert(sol.problem_ptr->variable_upper_bounds.size() == multi_probe.host_ub.size(), + // "size of variable upper bound mismatch"); + // raft::copy(multi_probe.host_ub.data(), + // sol.problem_ptr->variable_upper_bounds.data(), + // sol.problem_ptr->variable_upper_bounds.size(), + // sol.handle_ptr->get_stream()); + cuopt_assert(sol.problem_ptr->variable_bounds.size() == multi_probe.host_lb.size(), "size of variable lower bound mismatch"); - raft::copy(multi_probe.host_lb.data(), - sol.problem_ptr->variable_lower_bounds.data(), - sol.problem_ptr->variable_lower_bounds.size(), - sol.handle_ptr->get_stream()); - cuopt_assert(sol.problem_ptr->variable_upper_bounds.size() == multi_probe.host_ub.size(), + cuopt_assert(sol.problem_ptr->variable_bounds.size() == multi_probe.host_ub.size(), "size of variable upper bound mismatch"); - raft::copy(multi_probe.host_ub.data(), - sol.problem_ptr->variable_upper_bounds.data(), - sol.problem_ptr->variable_upper_bounds.size(), - sol.handle_ptr->get_stream()); + thrust::transform(sol.handle_ptr->get_thrust_policy(), + sol.problem_ptr->variable_bounds.begin(), + sol.problem_ptr->variable_bounds.end(), + thrust::make_zip_iterator( + thrust::make_tuple(multi_probe.host_lb.begin(), multi_probe.host_ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); } template @@ -739,17 +785,18 @@ void constraint_prop_t::restore_original_bounds_on_unfixed( problem_t& original_problem, const raft::handle_t* handle_ptr) { - thrust::for_each(handle_ptr->get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(problem.n_variables), - [p_v = problem.view(), op_v = original_problem.view()] __device__(i_t var_idx) { - if (!p_v.integer_equal(p_v.variable_lower_bounds[var_idx], - p_v.variable_upper_bounds[var_idx]) || - !p_v.is_integer_var(var_idx)) { - p_v.variable_lower_bounds[var_idx] = op_v.variable_lower_bounds[var_idx]; - p_v.variable_upper_bounds[var_idx] = op_v.variable_upper_bounds[var_idx]; - } - }); + thrust::for_each( + handle_ptr->get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(problem.n_variables), + [p_v = problem.view(), op_v = original_problem.view()] __device__(i_t var_idx) { + auto p_v_var_bnd = p_v.variable_bounds[var_idx]; + if (!p_v.integer_equal(p_v_var_bnd.x, p_v_var_bnd.y) || !p_v.is_integer_var(var_idx)) { + p_v.variable_bounds[var_idx] = op_v.variable_bounds[var_idx]; + // p_v.variable_lower_bounds[var_idx] = op_v.variable_lower_bounds[var_idx]; + // p_v.variable_upper_bounds[var_idx] = op_v.variable_upper_bounds[var_idx]; + } + }); } template @@ -948,16 +995,15 @@ bool constraint_prop_t::find_integer( rounding_ii = false; n_iter_in_recovery = 0; // during repair procedure some variables might be collapsed - auto iter = thrust::stable_partition( + using f_t2 = typename type_2::type; + auto iter = thrust::stable_partition( sol.handle_ptr->get_thrust_policy(), unset_vars.begin() + set_count, unset_vars.end(), - is_bound_fixed_t{orig_sol.problem_ptr->tolerances.integrality_tolerance, - make_span(sol.problem_ptr->variable_lower_bounds), - make_span(sol.problem_ptr->variable_upper_bounds), - make_span(orig_sol.problem_ptr->variable_lower_bounds), - make_span(orig_sol.problem_ptr->variable_upper_bounds), - make_span(sol.assignment)}); + is_bound_fixed_t{orig_sol.problem_ptr->tolerances.integrality_tolerance, + make_span(sol.problem_ptr->variable_bounds), + make_span(orig_sol.problem_ptr->variable_bounds), + make_span(sol.assignment)}); i_t n_fixed_vars = (iter - (unset_vars.begin() + set_count)); CUOPT_LOG_TRACE("After repair procedure, number of additional fixed vars %d", n_fixed_vars); set_count += n_fixed_vars; @@ -1097,10 +1143,14 @@ std::tuple constraint_prop_t::probing_values( "probing value out of bounds"); return std::make_tuple(first_round_val, var_val, second_round_val); } else { - auto orig_v_lb = - orig_sol.problem_ptr->variable_lower_bounds.element(idx, sol.handle_ptr->get_stream()); - auto orig_v_ub = - orig_sol.problem_ptr->variable_upper_bounds.element(idx, sol.handle_ptr->get_stream()); + // auto orig_v_lb = + // orig_sol.problem_ptr->variable_lower_bounds.element(idx, sol.handle_ptr->get_stream()); + // auto orig_v_ub = + // orig_sol.problem_ptr->variable_upper_bounds.element(idx, sol.handle_ptr->get_stream()); + auto orig_v_bnd = + orig_sol.problem_ptr->variable_bounds.element(idx, sol.handle_ptr->get_stream()); + auto orig_v_lb = orig_v_bnd.x; + auto orig_v_ub = orig_v_bnd.y; cuopt_assert(v_lb >= orig_v_lb, "Current lb should be greater than original lb"); cuopt_assert(v_ub <= orig_v_ub, "Current ub should be smaller than original ub"); v_lb = std::max(v_lb, orig_v_lb); @@ -1138,16 +1188,15 @@ bool constraint_prop_t::handle_fixed_vars( auto set_count = *set_count_ptr; const f_t int_tol = sol.problem_ptr->tolerances.integrality_tolerance; // which other variables were affected? - auto iter = thrust::stable_partition( + using f_t2 = typename type_2::type; + auto iter = thrust::stable_partition( sol.handle_ptr->get_thrust_policy(), unset_vars.begin() + set_count, unset_vars.end(), - is_bound_fixed_t{int_tol, - make_span(sol.problem_ptr->variable_lower_bounds), - make_span(sol.problem_ptr->variable_upper_bounds), - make_span(original_problem->variable_lower_bounds), - make_span(original_problem->variable_upper_bounds), - make_span(sol.assignment)}); + is_bound_fixed_t{int_tol, + make_span(sol.problem_ptr->variable_bounds), + make_span(original_problem->variable_bounds), + make_span(sol.assignment)}); i_t n_fixed_vars = (iter - (unset_vars.begin() + set_count)); cuopt_assert(n_fixed_vars >= std::get<0>(var_probe_vals).size(), "Error in number of vars fixed!"); diff --git a/cpp/src/mip/presolve/load_balanced_partition_helpers.cuh b/cpp/src/mip/presolve/load_balanced_partition_helpers.cuh index 55c18a902e..cffc5debb6 100644 --- a/cpp/src/mip/presolve/load_balanced_partition_helpers.cuh +++ b/cpp/src/mip/presolve/load_balanced_partition_helpers.cuh @@ -27,45 +27,6 @@ namespace cuopt::linear_programming::detail { -template -struct type_2 { - using type = void; -}; - -template <> -struct type_2 { - using type = int2; -}; - -template <> -struct type_2 { - using type = float2; -}; - -template <> -struct type_2 { - using type = double2; -}; - -template -raft::device_span::type> make_span_2(rmm::device_uvector& container) -{ - // TODO : ceildiv or throw assert - using T2 = typename type_2::type; - return raft::device_span(reinterpret_cast(container.data()), - sizeof(T) * container.size() / sizeof(T2)); -} - -template -raft::device_span::type> make_span_2( - rmm::device_uvector const& container) -{ - // TODO : ceildiv or throw assert - using T2 = typename type_2::type; - return raft::device_span(reinterpret_cast(container.data()), - sizeof(T) * container.size() / sizeof(T2)); -} - template constexpr int BitsPWrd = sizeof(degree_t) * 8; diff --git a/cpp/src/mip/problem/host_helper.cuh b/cpp/src/mip/problem/host_helper.cuh index 4023a32d27..297a827294 100644 --- a/cpp/src/mip/problem/host_helper.cuh +++ b/cpp/src/mip/problem/host_helper.cuh @@ -17,6 +17,7 @@ #pragma once +#include #include #include @@ -52,7 +53,9 @@ struct constraints_delta_t { template struct variables_delta_t { + using f_t2 = typename type_2::type; std::vector objective_coefficients; + std::vector variable_bounds; std::vector lower_bounds; std::vector upper_bounds; std::vector variable_types; @@ -66,6 +69,7 @@ struct variables_delta_t { i_t add_variable(f_t lower_bound, f_t upper_bound, f_t obj_weight, var_t var_type) { cuopt_assert(lower_bound >= 0, "Variable bounds must be non-negative!"); + variable_bounds.push_back(f_t2{lower_bound, upper_bound}); lower_bounds.push_back(lower_bound); upper_bounds.push_back(upper_bound); objective_coefficients.push_back(obj_weight); diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 888388ef18..43ea2b10f7 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -70,6 +70,8 @@ void problem_t::op_problem_cstr_body(const optimization_problem_tget_problem_category() != problem_category_t::LP; if (is_mip) { variable_types = @@ -123,6 +125,7 @@ problem_t::problem_t( offsets(problem_.get_constraint_matrix_offsets(), problem_.get_handle_ptr()->get_stream()), objective_coefficients(problem_.get_objective_coefficients(), problem_.get_handle_ptr()->get_stream()), + variable_bounds(0, problem_.get_handle_ptr()->get_stream()), variable_lower_bounds(problem_.get_variable_lower_bounds(), problem_.get_handle_ptr()->get_stream()), variable_upper_bounds(problem_.get_variable_upper_bounds(), @@ -175,6 +178,7 @@ problem_t::problem_t(const problem_t& problem_) variables(problem_.variables, handle_ptr->get_stream()), offsets(problem_.offsets, handle_ptr->get_stream()), objective_coefficients(problem_.objective_coefficients, handle_ptr->get_stream()), + variable_bounds(problem_.variable_bounds, handle_ptr->get_stream()), variable_lower_bounds(problem_.variable_lower_bounds, handle_ptr->get_stream()), variable_upper_bounds(problem_.variable_upper_bounds, handle_ptr->get_stream()), constraint_lower_bounds(problem_.constraint_lower_bounds, handle_ptr->get_stream()), @@ -248,6 +252,10 @@ problem_t::problem_t(const problem_t& problem_, bool no_deep ? rmm::device_uvector(problem_.objective_coefficients, handle_ptr->get_stream()) : rmm::device_uvector(problem_.objective_coefficients.size(), handle_ptr->get_stream())), + variable_bounds( + (!no_deep_copy) + ? rmm::device_uvector(problem_.variable_bounds, handle_ptr->get_stream()) + : rmm::device_uvector(problem_.variable_bounds.size(), handle_ptr->get_stream())), variable_lower_bounds( (!no_deep_copy) ? rmm::device_uvector(problem_.variable_lower_bounds, handle_ptr->get_stream()) @@ -392,7 +400,13 @@ void problem_t::check_problem_representation(bool check_transposed, if (!empty) { cuopt_assert(!variable_lower_bounds.is_empty() && !variable_upper_bounds.is_empty(), "Variable lower bounds and variable upper bounds must be set."); + cuopt_assert(!variable_bounds.is_empty(), "Variable bounds must be set."); } + cuopt_assert(variable_bounds.size() == 2 * objective_coefficients.size(), + "Sizes for vectors related to the variables are not the same."); + cuopt_assert(variable_bounds.size() == 2 * (std::size_t)n_variables, + "Sizes for vectors related to the variables are not the same."); + cuopt_assert(variable_lower_bounds.size() == objective_coefficients.size(), "Sizes for vectors related to the variables are not the same."); cuopt_assert(variable_upper_bounds.size() == objective_coefficients.size(), @@ -418,16 +432,15 @@ void problem_t::check_problem_representation(bool check_transposed, "Sizes for vectors related to the constraints are not the same."); // Check the validity of bounds - cuopt_expects( - thrust::all_of(handle_ptr->get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(n_variables), - [variable_lower_bounds = variable_lower_bounds.data(), - variable_upper_bounds = variable_upper_bounds.data()] __device__(i_t idx) { - return variable_lower_bounds[idx] <= variable_upper_bounds[idx]; - }), - error_type_t::ValidationError, - "Variable bounds are invalid"); + cuopt_expects(thrust::all_of(handle_ptr->get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(n_variables), + [vars_bnd = make_span(variable_bounds)] __device__(i_t idx) { + auto bounds = vars_bnd[idx]; + return bounds.x <= bounds.y; + }), + error_type_t::ValidationError, + "Variable bounds are invalid"); cuopt_expects( thrust::all_of(handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), @@ -581,14 +594,14 @@ void problem_t::check_problem_representation(bool check_transposed, is_binary_variable.cbegin()), thrust::make_zip_iterator(thrust::make_counting_iterator(is_binary_variable.size()), is_binary_variable.cend()), - [types = variable_types.data(), - lb = variable_lower_bounds.data(), - ub = variable_upper_bounds.data(), - v = view()] __device__(const thrust::tuple tuple) { - i_t idx = thrust::get<0>(tuple); - i_t pred = thrust::get<1>(tuple); - return pred == (types[idx] != var_t::CONTINUOUS && v.integer_equal(lb[idx], 0.) && - v.integer_equal(ub[idx], 1.)); + [types = variable_types.data(), + vars_bnd = make_span(variable_bounds), + v = view()] __device__(const thrust::tuple tuple) { + i_t idx = thrust::get<0>(tuple); + i_t pred = thrust::get<1>(tuple); + auto bounds = vars_bnd[idx]; + return pred == (types[idx] != var_t::CONTINUOUS && v.integer_equal(bounds.x, 0.) && + v.integer_equal(bounds.y, 1.)); }), "The binary variable table is incorrect."); if (!empty) { @@ -761,14 +774,15 @@ void problem_t::compute_binary_var_table() auto pb_view = view(); is_binary_variable.resize(n_variables, handle_ptr->get_stream()); - thrust::tabulate(handle_ptr->get_thrust_policy(), - is_binary_variable.begin(), - is_binary_variable.end(), - [pb_view] __device__(i_t i) { - return pb_view.variable_types[i] != var_t::CONTINUOUS && - (pb_view.integer_equal(pb_view.variable_lower_bounds[i], 0) && - pb_view.integer_equal(pb_view.variable_upper_bounds[i], 1)); - }); + thrust::tabulate( + handle_ptr->get_thrust_policy(), + is_binary_variable.begin(), + is_binary_variable.end(), + [pb_view] __device__(i_t i) { + auto bounds = pb_view.variable_bounds[i]; + return pb_view.variable_types[i] != var_t::CONTINUOUS && + (pb_view.integer_equal(bounds.x, 0) && pb_view.integer_equal(bounds.y, 1)); + }); get_n_binary_variables(); binary_indices.resize(n_variables, handle_ptr->get_stream()); @@ -916,6 +930,7 @@ typename problem_t::view_t problem_t::view() v.offsets = raft::device_span{offsets.data(), offsets.size()}; v.objective_coefficients = raft::device_span{objective_coefficients.data(), objective_coefficients.size()}; + v.variable_bounds = make_span(variable_bounds); v.variable_lower_bounds = raft::device_span{variable_lower_bounds.data(), variable_lower_bounds.size()}; v.variable_upper_bounds = @@ -942,6 +957,7 @@ typename problem_t::view_t problem_t::view() template void problem_t::resize_variables(size_t size) { + variable_bounds.resize(size, handle_ptr->get_stream()); variable_lower_bounds.resize(size, handle_ptr->get_stream()); variable_upper_bounds.resize(size, handle_ptr->get_stream()); variable_types.resize(size, handle_ptr->get_stream()); @@ -975,6 +991,10 @@ void problem_t::insert_variables(variables_delta_t& h_vars) CUOPT_LOG_DEBUG("problem added variable size %d prev %d", h_vars.size(), n_variables); // resize the variable arrays if it can't fit the variables resize_variables(n_variables + h_vars.size()); + raft::copy(variable_bounds.data() + n_variables, + h_vars.variable_bounds.data(), + h_vars.variable_bounds.size(), + handle_ptr->get_stream()); raft::copy(variable_lower_bounds.data() + n_variables, h_vars.lower_bounds.data(), h_vars.lower_bounds.size(), @@ -1218,6 +1238,12 @@ void problem_t::remove_given_variables(problem_t& original_p tmp_assignment.begin(), assignment.begin()); assignment.resize(variable_map.size(), handle_ptr->get_stream()); + thrust::gather(handle_ptr->get_thrust_policy(), + variable_map.begin(), + variable_map.end(), + original_problem.variable_bounds.begin(), + variable_bounds.begin()); + variable_bounds.resize(variable_map.size(), handle_ptr->get_stream()); thrust::gather(handle_ptr->get_thrust_policy(), variable_map.begin(), variable_map.end(), @@ -1346,21 +1372,23 @@ template void standardize_bounds(std::vector>>& variable_constraint_map, problem_t& pb) { - auto handle_ptr = pb.handle_ptr; - auto h_var_lower_bounds = cuopt::host_copy(pb.variable_lower_bounds); - auto h_var_upper_bounds = cuopt::host_copy(pb.variable_upper_bounds); + auto handle_ptr = pb.handle_ptr; + auto h_var_bounds = cuopt::host_copy(pb.variable_bounds); + // auto h_var_lower_bounds = cuopt::host_copy(pb.variable_lower_bounds); + // auto h_var_upper_bounds = cuopt::host_copy(pb.variable_upper_bounds); auto h_objective_coefficients = cuopt::host_copy(pb.objective_coefficients); auto h_variable_types = cuopt::host_copy(pb.variable_types); handle_ptr->sync_stream(); - const i_t n_vars_originally = (i_t)h_var_lower_bounds.size(); + const i_t n_vars_originally = (i_t)h_var_bounds.size(); for (i_t i = 0; i < n_vars_originally; ++i) { // if variable has free bounds, replace it with two vars // but add only one var and use it in all constraints // TODO create one var for integrals and one var for continuous - if (h_var_lower_bounds[i] == -std::numeric_limits::infinity() && - h_var_upper_bounds[i] == std::numeric_limits::infinity()) { + auto h_var_bound = h_var_bounds[i]; + if (h_var_bound.x == -std::numeric_limits::infinity() && + h_var_bound.y == std::numeric_limits::infinity()) { // add new variable auto var_coeff_vec = variable_constraint_map[i]; // negate all values in vec @@ -1368,16 +1396,16 @@ void standardize_bounds(std::vector>>& variable_ coeff = -coeff; } - h_var_lower_bounds[i] = 0.; + h_var_bounds[i].x = 0.; pb.presolve_data.variable_offsets[i] = 0.; pb.presolve_data.additional_var_used[i] = true; pb.presolve_data.additional_var_id_per_var[i] = pb.n_variables; + using f_t2 = typename type_2::type; // new var data std::stable_sort(var_coeff_vec.begin(), var_coeff_vec.end()); variable_constraint_map.push_back(var_coeff_vec); - h_var_lower_bounds.push_back(0.); - h_var_upper_bounds.push_back(std::numeric_limits::infinity()); + h_var_bounds.push_back(f_t2{0., std::numeric_limits::infinity()}); pb.presolve_data.variable_offsets.push_back(0.); h_objective_coefficients.push_back(-h_objective_coefficients[i]); h_variable_types.push_back(h_variable_types[i]); @@ -1393,21 +1421,24 @@ void standardize_bounds(std::vector>>& variable_ // TODO add some tests // resize the device vectors is sizes are smaller - if (pb.variable_lower_bounds.size() < h_var_lower_bounds.size()) { - pb.variable_lower_bounds.resize(h_var_lower_bounds.size(), handle_ptr->get_stream()); - pb.variable_upper_bounds.resize(h_var_lower_bounds.size(), handle_ptr->get_stream()); + if (pb.variable_bounds.size() < h_var_bounds.size()) { + // pb.variable_lower_bounds.resize(h_var_lower_bounds.size(), handle_ptr->get_stream()); + // pb.variable_upper_bounds.resize(h_var_lower_bounds.size(), handle_ptr->get_stream()); + pb.variable_bounds.resize(h_var_bounds.size(), handle_ptr->get_stream()); pb.objective_coefficients.resize(h_objective_coefficients.size(), handle_ptr->get_stream()); pb.variable_types.resize(h_variable_types.size(), handle_ptr->get_stream()); } - raft::copy(pb.variable_lower_bounds.data(), - h_var_lower_bounds.data(), - h_var_lower_bounds.size(), - handle_ptr->get_stream()); - raft::copy(pb.variable_upper_bounds.data(), - h_var_upper_bounds.data(), - h_var_upper_bounds.size(), - handle_ptr->get_stream()); + raft::copy( + pb.variable_bounds.data(), h_var_bounds.data(), h_var_bounds.size(), handle_ptr->get_stream()); + // raft::copy(pb.variable_lower_bounds.data(), + // h_var_lower_bounds.data(), + // h_var_lower_bounds.size(), + // handle_ptr->get_stream()); + // raft::copy(pb.variable_upper_bounds.data(), + // h_var_upper_bounds.data(), + // h_var_upper_bounds.size(), + // handle_ptr->get_stream()); raft::copy(pb.objective_coefficients.data(), h_objective_coefficients.data(), h_objective_coefficients.size(), @@ -1543,9 +1574,11 @@ void problem_t::get_host_user_problem( } } user_problem.num_range_rows = user_problem.range_rows.size(); - user_problem.lower = cuopt::host_copy(variable_lower_bounds); - user_problem.upper = cuopt::host_copy(variable_upper_bounds); - user_problem.problem_name = original_problem_ptr->get_problem_name(); + // user_problem.lower = cuopt::host_copy(variable_lower_bounds); + // user_problem.upper = cuopt::host_copy(variable_upper_bounds); + std::tie(user_problem.lower, user_problem.upper) = + extract_host_bounds(variable_bounds, handle_ptr); + user_problem.problem_name = original_problem_ptr->get_problem_name(); if (static_cast(row_names.size()) == m) { user_problem.row_names.resize(m); for (int i = 0; i < m; ++i) { diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index cc29ce0e65..f0ec7e430c 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -132,9 +132,9 @@ class problem_t { DI bool check_variable_within_bounds(i_t v, f_t val) const { - const f_t int_tol = tolerances.integrality_tolerance; - bool within_bounds = - val <= (variable_upper_bounds[v] + int_tol) && val >= (variable_lower_bounds[v] - int_tol); + const f_t int_tol = tolerances.integrality_tolerance; + auto bounds = variable_bounds[v]; + bool within_bounds = val <= (bounds.y + int_tol) && val >= (bounds.x - int_tol); return within_bounds; } @@ -155,21 +155,21 @@ class problem_t { { cuopt_assert(var_t::INTEGER != variable_types[v], "Random value can only be called on continuous values"); - f_t lower_bound = variable_lower_bounds[v]; - f_t upper_bound = variable_upper_bounds[v]; + auto bounds = variable_bounds[v]; f_t val; - if (isfinite(lower_bound) && isfinite(upper_bound)) { - f_t diff = upper_bound - lower_bound; - val = diff * rng.next_float() + lower_bound; + if (isfinite(bounds.x) && isfinite(bounds.y)) { + f_t diff = bounds.y - bounds.x; + val = diff * rng.next_float() + bounds.x; } else { - auto finite_bound = isfinite(lower_bound) ? lower_bound : upper_bound; + auto finite_bound = isfinite(bounds.x) ? bounds.x : bounds.y; val = finite_bound; } - cuopt_assert(isfinite(lower_bound), "Value must be finite"); + cuopt_assert(isfinite(bounds.x), "Value must be finite"); return val; } + using f_t2 = typename type_2::type; typename mip_solver_settings_t::tolerances_t tolerances; i_t n_variables; i_t n_integer_vars; @@ -184,6 +184,7 @@ class problem_t { raft::device_span variables; raft::device_span offsets; raft::device_span objective_coefficients; + raft::device_span variable_bounds; raft::device_span variable_lower_bounds; raft::device_span variable_upper_bounds; raft::device_span constraint_lower_bounds; @@ -239,6 +240,8 @@ class problem_t { /** weights in the objective function */ rmm::device_uvector objective_coefficients; + using f_t2 = typename type_2::type; + rmm::device_uvector variable_bounds; rmm::device_uvector variable_lower_bounds; rmm::device_uvector variable_upper_bounds; rmm::device_uvector constraint_lower_bounds; diff --git a/cpp/src/mip/problem/problem_helpers.cuh b/cpp/src/mip/problem/problem_helpers.cuh index dfcebc9d34..825ee0699b 100644 --- a/cpp/src/mip/problem/problem_helpers.cuh +++ b/cpp/src/mip/problem/problem_helpers.cuh @@ -58,6 +58,54 @@ struct transform_bounds_functor { } }; +#if 1 +template +static std::tuple, std::vector> extract_host_bounds( + const rmm::device_uvector& variable_bounds, const raft::handle_t* handle_ptr) +{ + rmm::device_uvector var_lb(variable_bounds.size(), handle_ptr->get_stream()); + rmm::device_uvector var_ub(variable_bounds.size(), handle_ptr->get_stream()); + thrust::transform(handle_ptr->get_thrust_policy(), + variable_bounds.begin(), + variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(var_lb.begin(), var_ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); + auto h_var_lb = cuopt::host_copy(var_lb); + auto h_var_ub = cuopt::host_copy(var_ub); + return std::make_tuple(h_var_lb, h_var_ub); +} + +template +static void set_variable_bounds(detail::problem_t& op_problem) +{ + op_problem.variable_bounds.resize(op_problem.n_variables, op_problem.handle_ptr->get_stream()); + auto vars_bnd = make_span(op_problem.variable_bounds); + + auto orig_problem = op_problem.original_problem_ptr; + auto variable_lower_bounds = make_span(orig_problem->get_variable_lower_bounds()); + auto variable_upper_bounds = make_span(orig_problem->get_variable_upper_bounds()); + + bool default_variable_lb = (orig_problem->get_variable_lower_bounds().is_empty()); + bool default_variable_ub = (orig_problem->get_variable_upper_bounds().is_empty()); + + thrust::for_each(op_problem.handle_ptr->get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(op_problem.n_variables), + [vars_bnd, + variable_lower_bounds, + variable_upper_bounds, + default_variable_lb, + default_variable_ub] __device__(auto i) { + using f_t2 = typename type_2::type; + auto lb = f_t{0}; + auto ub = std::numeric_limits::infinity(); + if (!default_variable_lb) { lb = variable_lower_bounds[i]; } + if (!default_variable_ub) { ub = variable_upper_bounds[i]; } + vars_bnd[i] = f_t2{lb, ub}; + }); +} +#endif + template static void set_bounds_if_not_set(detail::problem_t& op_problem) { @@ -90,6 +138,7 @@ static void set_bounds_if_not_set(detail::problem_t& op_problem) transform_bounds_functor()); } +#if 1 // If variable bound was not set, set it to default value if (op_problem.variable_lower_bounds.is_empty() && !op_problem.objective_coefficients.is_empty()) { @@ -109,6 +158,7 @@ static void set_bounds_if_not_set(detail::problem_t& op_problem) op_problem.variable_upper_bounds.end(), std::numeric_limits::infinity()); } +#endif if (op_problem.variable_types.is_empty() && !op_problem.objective_coefficients.is_empty()) { op_problem.variable_types.resize(op_problem.objective_coefficients.size(), op_problem.handle_ptr->get_stream()); diff --git a/cpp/src/utilities/copy_helpers.hpp b/cpp/src/utilities/copy_helpers.hpp index 611e7811aa..70fabc0abc 100644 --- a/cpp/src/utilities/copy_helpers.hpp +++ b/cpp/src/utilities/copy_helpers.hpp @@ -27,6 +27,46 @@ #include namespace cuopt { + +template +struct type_2 { + using type = void; +}; + +template <> +struct type_2 { + using type = int2; +}; + +template <> +struct type_2 { + using type = float2; +}; + +template <> +struct type_2 { + using type = double2; +}; + +template +raft::device_span::type> make_span_2(rmm::device_uvector& container) +{ + // TODO : ceildiv or throw assert + using T2 = typename type_2::type; + return raft::device_span(reinterpret_cast(container.data()), + sizeof(T) * container.size() / sizeof(T2)); +} + +template +raft::device_span::type> make_span_2( + rmm::device_uvector const& container) +{ + // TODO : ceildiv or throw assert + using T2 = typename type_2::type; + return raft::device_span(reinterpret_cast(container.data()), + sizeof(T) * container.size() / sizeof(T2)); +} + /** * @brief Simple utility function to copy device ptr to host * From 2035c9ae3cd35bf38aa8b9bcf54d80fc5772c6a9 Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Wed, 3 Sep 2025 09:10:15 -0400 Subject: [PATCH 02/10] in progress --- .../initial_scaling.cu | 19 ++-- cpp/src/linear_programming/pdhg.cu | 6 +- cpp/src/linear_programming/pdlp.cu | 14 --- .../infeasibility_information.cu | 8 -- cpp/src/linear_programming/translate.hpp | 9 +- .../utilities/problem_checking.cu | 62 +++++------- cpp/src/linear_programming/utils.cuh | 25 ++++- .../recombiners/bound_prop_recombiner.cuh | 5 +- .../mip/feasibility_jump/feasibility_jump.cu | 11 +-- .../mip/feasibility_jump/load_balancing.cuh | 16 ++-- .../local_search/rounding/bounds_repair.cu | 45 ++++----- .../local_search/rounding/bounds_repair.cuh | 23 +++-- .../local_search/rounding/constraint_prop.cu | 94 ++++++++++++++----- .../local_search/rounding/constraint_prop.cuh | 13 +++ .../rounding/simple_rounding_kernels.cuh | 5 +- .../conditional_bound_strengthening.cu | 10 +- cpp/src/mip/presolve/multi_probe.cu | 22 +++-- cpp/src/mip/presolve/multi_probe.cuh | 4 +- cpp/src/mip/presolve/probing_cache.cu | 44 +++++---- cpp/src/mip/problem/problem_helpers.cuh | 16 ---- cpp/src/mip/solution/feasibility_test.cuh | 8 +- cpp/src/mip/solution/solution.cu | 43 ++++++--- cpp/src/mip/solve.cu | 5 +- cpp/src/mip/utils.cuh | 24 ++--- cpp/src/utilities/copy_helpers.hpp | 37 ++++++++ cpp/tests/mip/load_balancing_test.cu | 23 +++-- 26 files changed, 339 insertions(+), 252 deletions(-) diff --git a/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cu b/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cu index 4c6cbf475b..72931267ad 100644 --- a/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cu +++ b/cpp/src/linear_programming/initial_scaling_strategy/initial_scaling.cu @@ -375,18 +375,13 @@ void pdlp_initial_scaling_strategy_t::scale_problem() primal_size_h_, stream_view_); - raft::linalg::eltwiseDivideCheckZero( - const_cast&>(op_problem_scaled_.variable_lower_bounds).data(), - op_problem_scaled_.variable_lower_bounds.data(), - cummulative_variable_scaling_.data(), - primal_size_h_, - stream_view_); - raft::linalg::eltwiseDivideCheckZero( - const_cast&>(op_problem_scaled_.variable_upper_bounds).data(), - op_problem_scaled_.variable_upper_bounds.data(), - cummulative_variable_scaling_.data(), - primal_size_h_, - stream_view_); + using f_t2 = typename type_2::type; + cub::DeviceTransform::Transform(cuda::std::make_tuple(op_problem_scaled_.variable_bounds.data(), + cummulative_variable_scaling_.data()), + op_problem_scaled_.variable_bounds.data(), + primal_size_h_, + divide_check_zero(), + stream_view_); raft::linalg::eltwiseMultiply( const_cast&>(op_problem_scaled_.constraint_lower_bounds).data(), diff --git a/cpp/src/linear_programming/pdhg.cu b/cpp/src/linear_programming/pdhg.cu index f932eeb8d8..38373391aa 100644 --- a/cpp/src/linear_programming/pdhg.cu +++ b/cpp/src/linear_programming/pdhg.cu @@ -143,18 +143,18 @@ void pdhg_solver_t::compute_primal_projection_with_gradient( // project by max(min(x[i], upperbound[i]),lowerbound[i]) // compute delta_primal x'-x + using f_t2 = typename type_2::type; // All is fused in a single call to limit number of read / write in memory cub::DeviceTransform::Transform( cuda::std::make_tuple(current_saddle_point_state_.get_primal_solution().data(), problem_ptr->objective_coefficients.data(), current_saddle_point_state_.get_current_AtY().data(), - problem_ptr->variable_lower_bounds.data(), - problem_ptr->variable_upper_bounds.data()), + problem_ptr->variable_bounds.data()), thrust::make_zip_iterator(potential_next_primal_solution_.data(), current_saddle_point_state_.get_delta_primal().data(), tmp_primal_.data()), primal_size_h_, - primal_projection(primal_step_size.data()), + primal_projection(primal_step_size.data()), stream_view_); } diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index c7cc09067b..824539e0ca 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -1041,20 +1041,6 @@ optimization_problem_solution_t pdlp_solver_t::run_solver( // Project initial primal solution if (pdlp_hyper_params::project_initial_primal) { - // raft::linalg::ternaryOp(pdhg_solver_.get_primal_solution().data(), - // pdhg_solver_.get_primal_solution().data(), - // op_problem_scaled_.variable_lower_bounds.data(), - // op_problem_scaled_.variable_upper_bounds.data(), - // primal_size_h_, - // clamp(), - // stream_view_); - // raft::linalg::ternaryOp(unscaled_primal_avg_solution_.data(), - // unscaled_primal_avg_solution_.data(), - // op_problem_scaled_.variable_lower_bounds.data(), - // op_problem_scaled_.variable_upper_bounds.data(), - // primal_size_h_, - // clamp(), - // stream_view_); using f_t2 = typename type_2::type; cub::DeviceTransform::Transform( cuda::std::make_tuple(pdhg_solver_.get_primal_solution().data(), diff --git a/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu b/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu index e96c19e70d..97a2235b5a 100644 --- a/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu +++ b/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu @@ -402,14 +402,6 @@ void infeasibility_information_t::compute_reduced_costs_dual_objective // Check if these bounds are the same as computed above // if reduced cost is positive -> lower bound, negative -> upper bounds, 0 -> 0 // if bound_val is not finite let element be -inf, otherwise bound_value*reduced_cost - // raft::linalg::binaryOp(bound_value_.data(), - // reduced_cost_.data(), - // //problem_ptr->variable_lower_bounds.data(), - // //problem_ptr->variable_upper_bounds.data(), - // problem_ptr->variable_bounds.data(), - // primal_size_h_, - // bound_value_reduced_cost_product(), - // stream_view_); cub::DeviceTransform::Transform( cuda::std::make_tuple(reduced_cost_.data(), problem_ptr->variable_bounds.data()), bound_value_.data(), diff --git a/cpp/src/linear_programming/translate.hpp b/cpp/src/linear_programming/translate.hpp index 6731be6ec3..0f1fc9f1c1 100644 --- a/cpp/src/linear_programming/translate.hpp +++ b/cpp/src/linear_programming/translate.hpp @@ -81,9 +81,9 @@ static dual_simplex::user_problem_t cuopt_problem_to_simplex_problem( } } user_problem.num_range_rows = user_problem.range_rows.size(); - user_problem.lower = cuopt::host_copy(model.variable_lower_bounds); - user_problem.upper = cuopt::host_copy(model.variable_upper_bounds); - user_problem.problem_name = model.original_problem_ptr->get_problem_name(); + std::tie(user_problem.lower, user_problem.upper) = + extract_host_bounds(model.variable_bounds, model.handle_ptr); + user_problem.problem_name = model.original_problem_ptr->get_problem_name(); if (model.row_names.size() > 0) { user_problem.row_names.resize(m); for (int i = 0; i < m; ++i) { @@ -164,8 +164,7 @@ void translate_to_crossover_problem(const detail::problem_t& problem, lp.obj_constant = problem.presolve_data.objective_offset; lp.obj_scale = problem.presolve_data.objective_scaling_factor; - std::vector lower = cuopt::host_copy(problem.variable_lower_bounds); - std::vector upper = cuopt::host_copy(problem.variable_upper_bounds); + auto [lower, upper] = extract_host_bounds(problem.variable_bounds, problem.handle_ptr); std::vector constraint_lower = cuopt::host_copy(problem.constraint_lower_bounds); std::vector constraint_upper = cuopt::host_copy(problem.constraint_upper_bounds); diff --git a/cpp/src/linear_programming/utilities/problem_checking.cu b/cpp/src/linear_programming/utilities/problem_checking.cu index d0fc6811b3..702741cc21 100644 --- a/cpp/src/linear_programming/utilities/problem_checking.cu +++ b/cpp/src/linear_programming/utilities/problem_checking.cu @@ -263,21 +263,16 @@ template void problem_checking_t::check_scaled_problem( detail::problem_t const& scaled_problem, detail::problem_t const& op_problem) { + using f_t2 = typename type_2::type; // original problem to host - auto& d_variable_upper_bounds = op_problem.variable_upper_bounds; - auto& d_variable_lower_bounds = op_problem.variable_lower_bounds; - auto& d_variable_types = op_problem.variable_types; - std::vector variable_upper_bounds(d_variable_upper_bounds.size()); - std::vector variable_lower_bounds(d_variable_lower_bounds.size()); + auto& d_variable_bounds = op_problem.variable_bounds; + auto& d_variable_types = op_problem.variable_types; + std::vector variable_bounds(d_variable_bounds.size()); std::vector variable_types(d_variable_types.size()); - raft::copy(variable_upper_bounds.data(), - d_variable_upper_bounds.data(), - d_variable_upper_bounds.size(), - op_problem.handle_ptr->get_stream()); - raft::copy(variable_lower_bounds.data(), - d_variable_lower_bounds.data(), - d_variable_lower_bounds.size(), + raft::copy(variable_bounds.data(), + d_variable_bounds.data(), + d_variable_bounds.size(), op_problem.handle_ptr->get_stream()); raft::copy(variable_types.data(), d_variable_types.data(), @@ -285,26 +280,18 @@ void problem_checking_t::check_scaled_problem( op_problem.handle_ptr->get_stream()); // scaled problem to host - std::vector scaled_variable_upper_bounds(scaled_problem.variable_upper_bounds.size()); - std::vector scaled_variable_lower_bounds(scaled_problem.variable_lower_bounds.size()); - std::vector scaled_variables(scaled_problem.variable_lower_bounds.size()); + std::vector scaled_variable_bounds(scaled_problem.variable_bounds.size()); - raft::copy(scaled_variable_upper_bounds.data(), - scaled_problem.variable_upper_bounds.data(), - scaled_problem.variable_upper_bounds.size(), - op_problem.handle_ptr->get_stream()); - raft::copy(scaled_variable_lower_bounds.data(), - scaled_problem.variable_lower_bounds.data(), - scaled_problem.variable_lower_bounds.size(), + raft::copy(scaled_variable_bounds.data(), + scaled_problem.variable_bounds.data(), + scaled_problem.variable_bounds.size(), op_problem.handle_ptr->get_stream()); for (size_t i = 0; i < variable_types.size(); ++i) { auto var_type = variable_types[i]; if (var_type == var_t::INTEGER) { // Integers should be untouched - cuopt_assert(variable_upper_bounds[i] == scaled_variable_upper_bounds[i], - "Mismatch upper scaling"); - cuopt_assert(variable_lower_bounds[i] == scaled_variable_lower_bounds[i], - "Mismatch lower scaling"); + cuopt_assert(variable_bounds[i].y == scaled_variable_bounds[i].y, "Mismatch upper scaling"); + cuopt_assert(variable_bounds[i].x == scaled_variable_bounds[i].x, "Mismatch lower scaling"); } } } @@ -313,26 +300,21 @@ template void problem_checking_t::check_unscaled_solution( detail::problem_t& op_problem, rmm::device_uvector const& assignment) { - auto& d_variable_upper_bounds = op_problem.variable_upper_bounds; - auto& d_variable_lower_bounds = op_problem.variable_lower_bounds; - std::vector variable_upper_bounds(d_variable_upper_bounds.size()); - std::vector variable_lower_bounds(d_variable_lower_bounds.size()); + using f_t2 = typename type_2::type; + auto& d_variable_bounds = op_problem.variable_bounds; + std::vector variable_bounds(d_variable_bounds.size()); std::vector h_assignment(assignment.size()); - raft::copy(variable_upper_bounds.data(), - d_variable_upper_bounds.data(), - d_variable_upper_bounds.size(), - op_problem.handle_ptr->get_stream()); - raft::copy(variable_lower_bounds.data(), - d_variable_lower_bounds.data(), - d_variable_lower_bounds.size(), + raft::copy(variable_bounds.data(), + d_variable_bounds.data(), + d_variable_bounds.size(), op_problem.handle_ptr->get_stream()); raft::copy( h_assignment.data(), assignment.data(), assignment.size(), op_problem.handle_ptr->get_stream()); const f_t int_tol = op_problem.tolerances.integrality_tolerance; - for (size_t i = 0; i < variable_upper_bounds.size(); ++i) { - cuopt_assert(h_assignment[i] <= variable_upper_bounds[i] + int_tol, "Excess upper bound"); - cuopt_assert(h_assignment[i] >= variable_lower_bounds[i] - int_tol, "Excess lower bound"); + for (size_t i = 0; i < variable_bounds.size(); ++i) { + cuopt_assert(h_assignment[i] <= variable_bounds[i].y + int_tol, "Excess upper bound"); + cuopt_assert(h_assignment[i] >= variable_bounds[i].x - int_tol, "Excess lower bound"); } } diff --git a/cpp/src/linear_programming/utils.cuh b/cpp/src/linear_programming/utils.cuh index bf685ef6df..34085b7df4 100644 --- a/cpp/src/linear_programming/utils.cuh +++ b/cpp/src/linear_programming/utils.cuh @@ -77,13 +77,17 @@ struct a_sub_scalar_times_b { const f_t* scalar_; }; -template +template struct primal_projection { primal_projection(const f_t* step_size) : step_size_(step_size) {} - __device__ __forceinline__ thrust::tuple operator()( - f_t primal, f_t obj_coeff, f_t AtY, f_t lower, f_t upper) + __device__ __forceinline__ thrust::tuple operator()(f_t primal, + f_t obj_coeff, + f_t AtY, + f_t2 bounds) { + f_t lower = bounds.x; + f_t upper = bounds.y; f_t gradient = obj_coeff - AtY; f_t next = primal - (*step_size_ * gradient); next = raft::max(raft::min(next, upper), lower); @@ -201,6 +205,21 @@ struct max_violation { } }; +template +struct divide_check_zero { + __device__ f_t2 operator()(f_t2 bounds, f_t value) + { + if (value == f_t{0}) { + bounds.x = f_t{0}; + bounds.y = f_t{0}; + } else { + bounds.x = bounds.x / value; + bounds.y = bounds.y / value; + } + return bounds; + } +}; + template struct bound_value_gradient { __device__ f_t operator()(f_t value, f_t2 bounds) diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index f38cc5759e..d9133f2993 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -86,8 +86,9 @@ class bound_prop_recombiner_t : public recombiner_t { f_t second_val = round(avg_val) == other_val ? guiding_val : round(avg_val); probing_values[idx] = thrust::make_pair(other_val, second_val); // assign some floating value, so that they can be rounded by bounds prop - f_t lb = guiding_view.problem.variable_lower_bounds[idx]; - f_t ub = guiding_view.problem.variable_upper_bounds[idx]; + auto bounds = guiding_view.problem.variable_bounds[idx]; + f_t lb = bounds.x; + f_t ub = bounds.y; if (integer_equal(lb, ub, int_tol)) { cuopt_assert(false, "The var values must be different in A and B!"); } else if (isfinite(lb)) { diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index fa88bddd19..15bf41bed3 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -273,9 +273,9 @@ void fj_t::device_init(const rmm::cuda_stream_view& stream) cuopt_assert(var_idx < pb.is_binary_variable.size(), ""); if (pb.is_binary_variable[var_idx]) { - cuopt_assert(pb.variable_lower_bounds[var_idx] == 0 && - pb.variable_upper_bounds[var_idx] == 1, - "invalid bounds for binary variable"); + cuopt_assert( + pb.variable_bounds[var_idx].x == 0 && pb.variable_bounds[var_idx].y == 1, + "invalid bounds for binary variable"); } }); } @@ -374,9 +374,8 @@ void fj_t::climber_init(i_t climber_idx, const rmm::cuda_stream_view& incumbent_assignment[var_idx] = round(incumbent_assignment[var_idx]); } // clamp to bounds - incumbent_assignment[var_idx] = - max(pb.variable_lower_bounds[var_idx], - min(pb.variable_upper_bounds[var_idx], incumbent_assignment[var_idx])); + auto bounds = pb.variable_bounds[var_idx]; + incumbent_assignment[var_idx] = max(bounds.x, min(bounds.y, incumbent_assignment[var_idx])); }); thrust::for_each( diff --git a/cpp/src/mip/feasibility_jump/load_balancing.cuh b/cpp/src/mip/feasibility_jump/load_balancing.cuh index f3515e5de0..7922d6e5f6 100644 --- a/cpp/src/mip/feasibility_jump/load_balancing.cuh +++ b/cpp/src/mip/feasibility_jump/load_balancing.cuh @@ -322,8 +322,8 @@ __global__ void load_balancing_compute_scores_binary( if (threadIdx.x == 0) { cuopt_assert(fj.incumbent_assignment[var_idx] == 0 || fj.incumbent_assignment[var_idx] == 1, "Current assignment is not binary!"); - cuopt_assert( - fj.pb.variable_lower_bounds[var_idx] == 0 && fj.pb.variable_upper_bounds[var_idx] == 1, ""); + cuopt_assert(fj.pb.variable_bounds[var_idx].x == 0 && fj.pb.variable_bounds[var_idx].y == 1, + ""); cuopt_assert( fj.pb.check_variable_within_bounds(var_idx, fj.incumbent_assignment[var_idx] + delta), "Var not within bounds!"); @@ -400,8 +400,9 @@ __global__ void load_balancing_mtm_compute_candidates( auto rcp_cstr_coeff = fj.cstr_coeff_reciprocal[csr_offset]; f_t c_lb = fj.constraint_lower_bounds_csr[csr_offset]; f_t c_ub = fj.constraint_upper_bounds_csr[csr_offset]; - f_t v_lb = fj.pb.variable_lower_bounds[var_idx]; - f_t v_ub = fj.pb.variable_upper_bounds[var_idx]; + auto v_bnd = fj.pb.variable_bounds[var_idx]; + f_t v_lb = v_bnd.x; + f_t v_ub = v_bnd.y; cuopt_assert(c_lb == fj.pb.constraint_lower_bounds[cstr_idx], ""); cuopt_assert(c_ub == fj.pb.constraint_upper_bounds[cstr_idx], ""); @@ -512,8 +513,9 @@ __launch_bounds__(TPB_loadbalance, 16) __global__ cuopt_assert(cstr_idx >= 0 && cstr_idx < fj.pb.n_constraints, ""); } - f_t v_lb = fj.pb.variable_lower_bounds[var_idx]; - f_t v_ub = fj.pb.variable_upper_bounds[var_idx]; + auto v_bnd = fj.pb.variable_bounds[var_idx]; + f_t v_lb = v_bnd.x; + f_t v_ub = v_bnd.y; // candidate counts is usually very small (<4) thanks to early duplicate deletion in the // previous kernel rarely limits the thoroughput nor leads to noticeable imbalance @@ -663,4 +665,4 @@ __global__ void load_balancing_sanity_checks(const __grid_constant__ __trap(); } } -} \ No newline at end of file +} diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index 4b9c0ca504..b4c53579e6 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -152,10 +152,12 @@ i_t bounds_repair_t::compute_best_shift(problem_t& problem, shift_amount = (down_vio / var_coeff); } if (shift_amount != 0.) { - f_t var_lb = pb_v.variable_lower_bounds[var_idx]; - f_t var_ub = pb_v.variable_upper_bounds[var_idx]; - f_t o_var_lb = o_pb_v.variable_lower_bounds[var_idx]; - f_t o_var_ub = o_pb_v.variable_upper_bounds[var_idx]; + auto var_bnd = pb_v.variable_bounds[var_idx]; + auto o_var_bnd = o_pb_v.variable_bounds[var_idx]; + f_t var_lb = var_bnd.x; + f_t var_ub = var_bnd.y; + f_t o_var_lb = o_var_bnd.x; + f_t o_var_ub = o_var_bnd.y; cuopt_assert(var_lb + pb_v.tolerances.integrality_tolerance >= o_var_lb, ""); cuopt_assert(o_var_ub + pb_v.tolerances.integrality_tolerance >= var_ub, ""); // round the shift amount of integer @@ -211,8 +213,9 @@ __global__ void compute_damages_kernel(typename problem_t::view_t prob { i_t var_idx = candidates.variable_index[blockIdx.x]; f_t shift_amount = candidates.bound_shift[blockIdx.x]; - f_t v_lb = problem.variable_lower_bounds[var_idx]; - f_t v_ub = problem.variable_upper_bounds[var_idx]; + auto v_bnd = problem.variable_bounds[var_idx]; + f_t v_lb = v_bnd.x; + f_t v_ub = v_bnd.y; f_t th_damage = 0.; i_t n_infeasible_cstr_delta = 0; auto [offset_begin, offset_end] = problem.reverse_range_for_var(var_idx); @@ -356,28 +359,28 @@ void bounds_repair_t::apply_move(problem_t& problem, original_problem = original_problem.view()] __device__() { i_t var_idx = candidates.variable_index[move_idx]; f_t shift_value = candidates.bound_shift[move_idx]; + auto bounds = problem.variable_bounds[var_idx]; DEVICE_LOG_TRACE("Applying move on var %d with shift %f lb %f ub %f o_lb %f o_ub %f \n", var_idx, shift_value, - problem.variable_lower_bounds[var_idx], - problem.variable_upper_bounds[var_idx], - original_problem.variable_lower_bounds[var_idx], - original_problem.variable_upper_bounds[var_idx]); - if (problem.integer_equal(problem.variable_lower_bounds[var_idx], - problem.variable_upper_bounds[var_idx])) { + bounds.x, + bounds.y, + original_problem.variable_bounds[var_idx].x, + original_problem.variable_bounds[var_idx].y); + if (problem.integer_equal(bounds.x, bounds.y)) { *candidates.at_least_one_singleton_moved = 1; } - problem.variable_lower_bounds[var_idx] += shift_value; - problem.variable_upper_bounds[var_idx] += shift_value; + bounds.x += shift_value; + bounds.y += shift_value; + problem.variable_bounds[var_idx] = bounds; + cuopt_assert(original_problem.variable_bounds[var_idx].x <= + bounds.y + problem.tolerances.integrality_tolerance, + ""); cuopt_assert( - original_problem.variable_lower_bounds[var_idx] <= - problem.variable_lower_bounds[var_idx] + problem.tolerances.integrality_tolerance, + original_problem.variable_bounds[var_idx].y + problem.tolerances.integrality_tolerance >= + bounds.y, ""); - cuopt_assert(original_problem.variable_upper_bounds[var_idx] + - problem.tolerances.integrality_tolerance >= - problem.variable_upper_bounds[var_idx], - ""); }); } @@ -474,4 +477,4 @@ template class bounds_repair_t; template class bounds_repair_t; #endif -}; // namespace cuopt::linear_programming::detail \ No newline at end of file +}; // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cuh b/cpp/src/mip/local_search/rounding/bounds_repair.cuh index 2a57d06600..dec907c761 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cuh +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cuh @@ -43,17 +43,24 @@ struct bounds_t { } void update_from(const problem_t& pb, const raft::handle_t* handle_ptr) { - cuopt_assert(lb.size() == pb.variable_lower_bounds.size(), ""); - cuopt_assert(ub.size() == pb.variable_upper_bounds.size(), ""); - raft::copy(lb.data(), pb.variable_lower_bounds.data(), lb.size(), handle_ptr->get_stream()); - raft::copy(ub.data(), pb.variable_upper_bounds.data(), ub.size(), handle_ptr->get_stream()); + cuopt_assert(lb.size() == pb.variable_bounds.size(), ""); + cuopt_assert(ub.size() == pb.variable_bounds.size(), ""); + thrust::transform(handle_ptr->get_thrust_policy(), + pb.variable_bounds.begin(), + pb.variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(lb.begin(), ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); }; void update_to(problem_t& pb, const raft::handle_t* handle_ptr) { - cuopt_assert(lb.size() == pb.variable_lower_bounds.size(), ""); - cuopt_assert(ub.size() == pb.variable_upper_bounds.size(), ""); - raft::copy(pb.variable_lower_bounds.data(), lb.data(), lb.size(), handle_ptr->get_stream()); - raft::copy(pb.variable_upper_bounds.data(), ub.data(), ub.size(), handle_ptr->get_stream()); + cuopt_assert(lb.size() == pb.variable_bounds.size(), ""); + cuopt_assert(ub.size() == pb.variable_bounds.size(), ""); + using f_t2 = typename type_2::type; + thrust::transform(handle_ptr->get_thrust_policy(), + thrust::make_zip_iterator(thrust::make_tuple(lb.begin(), ub.begin())), + thrust::make_zip_iterator(thrust::make_tuple(lb.end(), ub.end())), + pb.variable_bounds.begin(), + [] __device__(auto i) { return f_t2{thrust::get<0>(i), thrust::get<1>(i)}; }); }; rmm::device_uvector lb; rmm::device_uvector ub; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 6eb7daad73..50fed7b801 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -522,6 +522,36 @@ struct greater_than_threshold_t { __host__ __device__ bool operator()(const i_t& x) const { return assignment[x] > threshold; } }; +template +template +void constraint_prop_t::copy_bounds(rmm::device_uvector& output_bounds, + const rmm::device_uvector& input_lb, + const rmm::device_uvector& input_ub, + const raft::handle_t* handle_ptr) +{ + thrust::transform( + handle_ptr->get_thrust_policy(), + thrust::make_zip_iterator(thrust::make_tuple(input_lb.begin(), input_ub.begin())), + thrust::make_zip_iterator(thrust::make_tuple(input_lb.end(), input_ub.end())), + output_bounds.begin(), + [] __device__(auto bounds) { return f_t2{thrust::get<0>(bounds), thrust::get<1>(bounds)}; }); +} + +template +template +void constraint_prop_t::copy_bounds(rmm::device_uvector& output_lb, + rmm::device_uvector& output_ub, + const rmm::device_uvector& input_bounds, + const raft::handle_t* handle_ptr) +{ + thrust::transform( + handle_ptr->get_thrust_policy(), + input_bounds.begin(), + input_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(output_lb.begin(), output_ub.begin())), + [] __device__(auto bounds) { return thrust::make_tuple(bounds.x, bounds.y); }); +} + template void constraint_prop_t::copy_bounds(rmm::device_uvector& output_lb, rmm::device_uvector& output_ub, @@ -552,38 +582,56 @@ void constraint_prop_t::copy_bounds(rmm::device_uvector& output_l template void constraint_prop_t::save_bounds(solution_t& sol) { - copy_bounds(lb_restore, - ub_restore, - assignment_restore, - sol.problem_ptr->variable_lower_bounds, - sol.problem_ptr->variable_upper_bounds, - sol.assignment, - sol.handle_ptr); + // copy_bounds(lb_restore, + // ub_restore, + // assignment_restore, + // sol.problem_ptr->variable_lower_bounds, + // sol.problem_ptr->variable_upper_bounds, + // sol.assignment, + // sol.handle_ptr); + copy_bounds(lb_restore, ub_restore, sol.problem_ptr->variable_bounds, sol.handle_ptr); + raft::copy(assignment_restore.data(), + sol.assignment.data(), + sol.assignment.size(), + sol.handle_ptr->get_stream()); } template void constraint_prop_t::restore_bounds(solution_t& sol) { - copy_bounds(sol.problem_ptr->variable_lower_bounds, - sol.problem_ptr->variable_upper_bounds, - sol.assignment, - lb_restore, - ub_restore, - assignment_restore, - sol.handle_ptr); + // copy_bounds(sol.problem_ptr->variable_lower_bounds, + // sol.problem_ptr->variable_upper_bounds, + // sol.assignment, + // lb_restore, + // ub_restore, + // assignment_restore, + // sol.handle_ptr); + copy_bounds(sol.problem_ptr->variable_bounds, lb_restore, ub_restore, sol.handle_ptr); + raft::copy(sol.assignment.data(), + assignment_restore.data(), + assignment_restore.size(), + sol.handle_ptr->get_stream()); } template void constraint_prop_t::restore_original_bounds(solution_t& sol, solution_t& orig_sol) { - copy_bounds(sol.problem_ptr->variable_lower_bounds, - sol.problem_ptr->variable_upper_bounds, - sol.assignment, - orig_sol.problem_ptr->variable_lower_bounds, - orig_sol.problem_ptr->variable_upper_bounds, - orig_sol.assignment, - orig_sol.handle_ptr); + // copy_bounds(sol.problem_ptr->variable_lower_bounds, + // sol.problem_ptr->variable_upper_bounds, + // sol.assignment, + // orig_sol.problem_ptr->variable_lower_bounds, + // orig_sol.problem_ptr->variable_upper_bounds, + // orig_sol.assignment, + // orig_sol.handle_ptr); + raft::copy(sol.problem_ptr->variable_bounds.data(), + orig_sol.problem_ptr->variable_bounds.data(), + orig_sol.problem_ptr->variable_bounds.size(), + orig_sol.handle_ptr->get_stream()); + raft::copy(sol.assignment.data(), + orig_sol.assignment.data(), + orig_sol.assignment.size(), + orig_sol.handle_ptr->get_stream()); } template @@ -1030,9 +1078,7 @@ bool constraint_prop_t::find_integer( // we update from the problem bounds and not the final bounds of bounds update // because we might be in a recovery mode where we want to continue with the bounds before bulk // which is the unchanged problem bounds - multi_probe.update_host_bounds(sol.handle_ptr, - make_span(sol.problem_ptr->variable_lower_bounds), - make_span(sol.problem_ptr->variable_upper_bounds)); + multi_probe.update_host_bounds(sol.handle_ptr, make_span(sol.problem_ptr->variable_bounds)); } CUOPT_LOG_DEBUG( "Bounds propagation rounding end: ii constraint count first buffer %d, second buffer %d", diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cuh b/cpp/src/mip/local_search/rounding/constraint_prop.cuh index cb9cbbd00b..451822519e 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cuh +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cuh @@ -95,6 +95,19 @@ struct constraint_prop_t { void sort_by_frac(solution_t& sol, raft::device_span vars); void restore_bounds(solution_t& sol); void save_bounds(solution_t& sol); + + template + void copy_bounds(rmm::device_uvector& output_lb, + rmm::device_uvector& output_ub, + const rmm::device_uvector& input_bounds, + const raft::handle_t* handle_ptr); + + template + void copy_bounds(rmm::device_uvector& output_bounds, + const rmm::device_uvector& input_lb, + const rmm::device_uvector& input_ub, + const raft::handle_t* handle_ptr); + void copy_bounds(rmm::device_uvector& output_lb, rmm::device_uvector& output_ub, const rmm::device_uvector& input_lb, diff --git a/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh b/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh index 9c1ac7f3b1..29edf3e16d 100644 --- a/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh +++ b/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh @@ -36,8 +36,9 @@ __global__ void nearest_rounding_kernel(typename solution_t::view_t so f_t curr_val = solution.assignment[var_id]; if (solution.problem.is_integer(curr_val)) { return; } const f_t int_tol = solution.problem.tolerances.integrality_tolerance; - f_t lb = solution.problem.variable_lower_bounds[var_id]; - f_t ub = solution.problem.variable_upper_bounds[var_id]; + auto var_bnd = solution.problem.variable_bounds[var_id]; + f_t lb = var_bnd.x; + f_t ub = var_bnd.y; f_t nearest_val = round_nearest(curr_val, lb, ub, int_tol, rng); solution.assignment[var_id] = nearest_val; } diff --git a/cpp/src/mip/presolve/conditional_bound_strengthening.cu b/cpp/src/mip/presolve/conditional_bound_strengthening.cu index 2723a9b244..bf04088d54 100644 --- a/cpp/src/mip/presolve/conditional_bound_strengthening.cu +++ b/cpp/src/mip/presolve/conditional_bound_strengthening.cu @@ -550,8 +550,9 @@ __global__ void update_constraint_bounds_kernel(typename problem_t::vi if (tid < n_variables_in_constraint) { i_t variable_j = pb.variables[offset_j + tid]; a[tid] = pb.coefficients[offset_j + tid]; - lb[tid] = pb.variable_lower_bounds[variable_j]; - ub[tid] = pb.variable_upper_bounds[variable_j]; + auto bounds = pb.variable_bounds[variable_j]; + lb[tid] = bounds.x; + ub[tid] = bounds.y; vtypes[tid] = pb.variable_types[variable_j]; c[tid] = 0.; @@ -575,8 +576,9 @@ __global__ void update_constraint_bounds_kernel(typename problem_t::vi if (jj < 0) { f_t coeff = pb.coefficients[offset_i + index]; - f_t li = pb.variable_lower_bounds[variable_i]; - f_t ui = pb.variable_upper_bounds[variable_i]; + auto bounds = pb.variable_bounds[variable_i]; + f_t li = bounds.x; + f_t ui = bounds.y; min_activity_if_not_participating += (coeff > 0. ? coeff * li : coeff * ui); max_activity_if_not_participating += (coeff > 0. ? coeff * ui : coeff * li); } diff --git a/cpp/src/mip/presolve/multi_probe.cu b/cpp/src/mip/presolve/multi_probe.cu index 699a5f1dd4..0d972baae2 100644 --- a/cpp/src/mip/presolve/multi_probe.cu +++ b/cpp/src/mip/presolve/multi_probe.cu @@ -331,14 +331,24 @@ void multi_probe_t::update_device_bounds(const raft::handle_t* handle_ } template +template void multi_probe_t::update_host_bounds(const raft::handle_t* handle_ptr, - const raft::device_span variable_lb, - const raft::device_span variable_ub) + const raft::device_span variable_bounds) { - cuopt_assert(variable_lb.size() == host_lb.size(), "size of variable lower bound mismatch"); - raft::copy(host_lb.data(), variable_lb.data(), variable_lb.size(), handle_ptr->get_stream()); - cuopt_assert(variable_ub.size() == host_ub.size(), "size of variable upper bound mismatch"); - raft::copy(host_ub.data(), variable_ub.data(), variable_ub.size(), handle_ptr->get_stream()); + cuopt_assert(variable_bounds.size() == host_lb.size(), "size of variable lower bound mismatch"); + // raft::copy(host_lb.data(), variable_lb.data(), variable_lb.size(), handle_ptr->get_stream()); + cuopt_assert(variable_bounds.size() == host_ub.size(), "size of variable upper bound mismatch"); + // raft::copy(host_ub.data(), variable_ub.data(), variable_ub.size(), handle_ptr->get_stream()); + + rmm::device_uvector var_lb(variable_bounds.size(), handle_ptr->get_stream()); + rmm::device_uvector var_ub(variable_bounds.size(), handle_ptr->get_stream()); + thrust::transform(handle_ptr->get_thrust_policy(), + variable_bounds.begin(), + variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(var_lb.begin(), var_ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); + raft::copy(host_lb.data(), var_lb.data(), var_lb.size(), handle_ptr->get_stream()); + raft::copy(host_ub.data(), var_ub.data(), var_ub.size(), handle_ptr->get_stream()); } template diff --git a/cpp/src/mip/presolve/multi_probe.cuh b/cpp/src/mip/presolve/multi_probe.cuh index 609702e909..61e710cd6d 100644 --- a/cpp/src/mip/presolve/multi_probe.cuh +++ b/cpp/src/mip/presolve/multi_probe.cuh @@ -71,9 +71,9 @@ class multi_probe_t { const raft::handle_t* handle_ptr); void constraint_stats(problem_t& pb, const raft::handle_t* handle_ptr); void copy_problem_into_probing_buffers(problem_t& pb, const raft::handle_t* handle_ptr); + template void update_host_bounds(const raft::handle_t* handle_ptr, - const raft::device_span variable_lb, - const raft::device_span variable_ub); + const raft::device_span variable_bounds); void update_device_bounds(const raft::handle_t* handle_ptr); mip_solver_context_t& context; bounds_update_data_t upd_0; diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index 36140c5b31..91f6636f0f 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -155,12 +155,11 @@ bool probing_cache_t::contains(problem_t& problem, i_t var_i return probing_cache.count(problem.original_ids[var_id]) > 0; } -template +template void inline insert_current_probing_to_cache(i_t var_idx, const val_interval_t& probe_val, bound_presolve_t& bound_presolve, - const std::vector& original_lb, - const std::vector& original_ub, + const std::vector& original_bounds, const std::vector& modified_lb, const std::vector& modified_ub, const std::vector& h_integer_indices, @@ -171,15 +170,16 @@ void inline insert_current_probing_to_cache(i_t var_idx, cache_entry_t cache_item; cache_item.val_interval = probe_val; for (auto impacted_var_idx : h_integer_indices) { - if (original_lb[impacted_var_idx] != modified_lb[impacted_var_idx] || - original_ub[impacted_var_idx] != modified_ub[impacted_var_idx]) { + auto original_var_bounds = original_bounds[impacted_var_idx]; + if (original_var_bounds.x != modified_lb[impacted_var_idx] || + original_var_bounds.y != modified_ub[impacted_var_idx]) { if (integer_equal( modified_lb[impacted_var_idx], modified_ub[impacted_var_idx], int_tol)) { ++n_implied_singletons; } - cuopt_assert(modified_lb[impacted_var_idx] >= original_lb[impacted_var_idx], + cuopt_assert(modified_lb[impacted_var_idx] >= original_var_bounds.x, "Lower bound must be greater than or equal to original lower bound"); - cuopt_assert(modified_ub[impacted_var_idx] <= original_ub[impacted_var_idx], + cuopt_assert(modified_ub[impacted_var_idx] <= original_var_bounds.y, "Upper bound must be less than or equal to original upper bound"); cached_bound_t new_bound{modified_lb[impacted_var_idx], modified_ub[impacted_var_idx]}; cache_item.var_to_cached_bound_map.insert({impacted_var_idx, new_bound}); @@ -210,8 +210,9 @@ __global__ void compute_min_slack_per_var(typename problem_t::view_t p i_t var_offset = pb.reverse_offsets[var_idx]; i_t var_degree = pb.reverse_offsets[var_idx + 1] - var_offset; f_t th_var_unit_slack = std::numeric_limits::max(); - f_t lb = pb.variable_lower_bounds[var_idx]; - f_t ub = pb.variable_upper_bounds[var_idx]; + auto var_bounds = pb.variable_bounds[var_idx]; + f_t lb = var_bounds.x; + f_t ub = var_bounds.y; f_t first_coeff = pb.reverse_coefficients[var_offset]; bool different_coeff = false; for (i_t i = threadIdx.x; i < var_degree; i += blockDim.x) { @@ -360,13 +361,12 @@ inline std::vector compute_prioritized_integer_indices( return h_priority_indices; } -template +template void compute_cache_for_var(i_t var_idx, bound_presolve_t& bound_presolve, problem_t& problem, multi_probe_t& multi_probe_presolve, - const std::vector& h_var_lower_bounds, - const std::vector& h_var_upper_bounds, + const std::vector& h_var_bounds, const std::vector& h_integer_indices, std::atomic& n_of_implied_singletons, std::atomic& n_of_cached_probings, @@ -375,11 +375,12 @@ void compute_cache_for_var(i_t var_idx, RAFT_CUDA_TRY(cudaSetDevice(device_id)); // test if we need per thread handle raft::handle_t handle{}; - std::vector h_improved_lower_bounds(h_var_lower_bounds.size()); - std::vector h_improved_upper_bounds(h_var_upper_bounds.size()); + std::vector h_improved_lower_bounds(h_var_bounds.size()); + std::vector h_improved_upper_bounds(h_var_bounds.size()); std::pair, val_interval_t> probe_vals; - f_t lb = h_var_lower_bounds[var_idx]; - f_t ub = h_var_upper_bounds[var_idx]; + auto bounds = h_var_bounds[var_idx]; + f_t lb = bounds.x; + f_t ub = bounds.y; for (i_t i = 0; i < 2; ++i) { auto& probe_val = i == 0 ? probe_vals.first : probe_vals.second; // if binary, probe both values @@ -451,8 +452,7 @@ void compute_cache_for_var(i_t var_idx, insert_current_probing_to_cache(var_idx, probe_val, bound_presolve, - h_var_lower_bounds, - h_var_upper_bounds, + h_var_bounds, h_improved_lower_bounds, h_improved_upper_bounds, h_integer_indices, @@ -470,9 +470,8 @@ void compute_probing_cache(bound_presolve_t& bound_presolve, // we dont want to compute the probing cache for all variables for time and computation resources auto priority_indices = compute_prioritized_integer_indices(bound_presolve, problem); CUOPT_LOG_DEBUG("Computing probing cache"); - auto h_integer_indices = host_copy(problem.integer_indices); - const auto h_var_upper_bounds = host_copy(problem.variable_upper_bounds); - const auto h_var_lower_bounds = host_copy(problem.variable_lower_bounds); + auto h_integer_indices = host_copy(problem.integer_indices); + const auto h_var_bounds = host_copy(problem.variable_bounds); // TODO adjust the iteration limit depending on the total time limit and time it takes for single // var bound_presolve.settings.iteration_limit = 50; @@ -512,8 +511,7 @@ void compute_probing_cache(bound_presolve_t& bound_presolve, bound_presolve, problem, multi_probe_presolve, - h_var_lower_bounds, - h_var_upper_bounds, + h_var_bounds, h_integer_indices, n_of_implied_singletons, n_of_cached_probings, diff --git a/cpp/src/mip/problem/problem_helpers.cuh b/cpp/src/mip/problem/problem_helpers.cuh index 825ee0699b..ca9a982f12 100644 --- a/cpp/src/mip/problem/problem_helpers.cuh +++ b/cpp/src/mip/problem/problem_helpers.cuh @@ -59,22 +59,6 @@ struct transform_bounds_functor { }; #if 1 -template -static std::tuple, std::vector> extract_host_bounds( - const rmm::device_uvector& variable_bounds, const raft::handle_t* handle_ptr) -{ - rmm::device_uvector var_lb(variable_bounds.size(), handle_ptr->get_stream()); - rmm::device_uvector var_ub(variable_bounds.size(), handle_ptr->get_stream()); - thrust::transform(handle_ptr->get_thrust_policy(), - variable_bounds.begin(), - variable_bounds.end(), - thrust::make_zip_iterator(thrust::make_tuple(var_lb.begin(), var_ub.begin())), - [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); - auto h_var_lb = cuopt::host_copy(var_lb); - auto h_var_ub = cuopt::host_copy(var_ub); - return std::make_tuple(h_var_lb, h_var_ub); -} - template static void set_variable_bounds(detail::problem_t& op_problem) { diff --git a/cpp/src/mip/solution/feasibility_test.cuh b/cpp/src/mip/solution/feasibility_test.cuh index eafb8a67cb..cde7eafbd5 100644 --- a/cpp/src/mip/solution/feasibility_test.cuh +++ b/cpp/src/mip/solution/feasibility_test.cuh @@ -55,8 +55,8 @@ __global__ void test_variable_bounds_kernel(typename solution_t::view_ printf("inf var %d val %f l %f u %f integer %d\n", v, val, - sol.problem.variable_lower_bounds[v], - sol.problem.variable_upper_bounds[v], + sol.problem.variable_bounds[v].x, + sol.problem.variable_bounds[v].y, sol.problem.is_integer_var(v)); } cuopt_assert(isfinite(val), "assignment should be finite!"); @@ -72,8 +72,8 @@ __global__ void test_variable_bounds_kernel(typename solution_t::view_ printf("oob var %d val %f l %f u %f integer %d\n", v, val, - sol.problem.variable_lower_bounds[v], - sol.problem.variable_upper_bounds[v], + sol.problem.variable_bounds[v].x, + sol.problem.variable_bounds[v].y, sol.problem.is_integer_var(v)); } cuopt_assert(feasible, "Variables should be feasible"); diff --git a/cpp/src/mip/solution/solution.cu b/cpp/src/mip/solution/solution.cu index b3a7f6dbbf..82fdadcbef 100644 --- a/cpp/src/mip/solution/solution.cu +++ b/cpp/src/mip/solution/solution.cu @@ -35,11 +35,25 @@ namespace cuopt::linear_programming::detail { +template +rmm::device_uvector get_lower_bounds(rmm::device_uvector::type>& bounds, + const raft::handle_t* handle_ptr) +{ + using f_t2 = typename type_2::type; + rmm::device_uvector lower_bounds(bounds.size(), handle_ptr->get_stream()); + thrust::transform(handle_ptr->get_thrust_policy(), + bounds.begin(), + bounds.end(), + lower_bounds.begin(), + [] __device__(auto bnd) { return bnd.x; }); + return lower_bounds; +} + template solution_t::solution_t(problem_t& problem_) : problem_ptr(&problem_), handle_ptr(problem_.handle_ptr), - assignment(problem_.variable_lower_bounds, handle_ptr->get_stream()), + assignment(std::move(get_lower_bounds(problem_.variable_bounds, handle_ptr))), lower_excess(problem_.n_constraints, handle_ptr->get_stream()), upper_excess(problem_.n_constraints, handle_ptr->get_stream()), lower_slack(problem_.n_constraints, handle_ptr->get_stream()), @@ -220,16 +234,16 @@ void solution_t::assign_random_within_bounds(f_t ratio_of_vars_to_rand std::vector h_assignment = host_copy(assignment); std::uniform_real_distribution unif_prob(0, 1); - auto variable_lower_bounds = cuopt::host_copy(problem_ptr->variable_lower_bounds); - auto variable_upper_bounds = cuopt::host_copy(problem_ptr->variable_upper_bounds); - auto variable_types = cuopt::host_copy(problem_ptr->variable_types); + auto variable_bounds = cuopt::host_copy(problem_ptr->variable_bounds); + auto variable_types = cuopt::host_copy(problem_ptr->variable_types); problem_ptr->handle_ptr->sync_stream(); - for (size_t i = 0; i < problem_ptr->variable_lower_bounds.size(); ++i) { + for (size_t i = 0; i < problem_ptr->variable_bounds.size(); ++i) { if (only_integers && variable_types[i] != var_t::INTEGER) { continue; } bool skip = unif_prob(rng) > ratio_of_vars_to_random_assign; if (skip) { continue; } - f_t lower_bound = variable_lower_bounds[i]; - f_t upper_bound = variable_upper_bounds[i]; + auto var_bounds = variable_bounds[i]; + auto lower_bound = var_bounds.x; + auto upper_bound = var_bounds.y; if (lower_bound == -std::numeric_limits::infinity()) { h_assignment[i] = upper_bound; } else if (upper_bound == std::numeric_limits::infinity()) { @@ -443,10 +457,11 @@ i_t solution_t::calculate_similarity_radius(solution_t& othe problem_ptr->integer_indices.end(), cuda::proclaim_return_type( [other_ptr, curr_assignment, p_view = problem_ptr->view()] __device__(i_t idx) -> bool { + auto var_bounds = p_view.variable_bounds[idx]; return diverse_equal(other_ptr[idx], curr_assignment[idx], - p_view.variable_lower_bounds[idx], - p_view.variable_upper_bounds[idx], + var_bounds.x, + var_bounds.y, p_view.is_integer_var(idx), p_view.tolerances.integrality_tolerance); })); @@ -542,17 +557,15 @@ template f_t solution_t::compute_max_variable_violation() { cuopt_assert(problem_ptr->n_variables == assignment.size(), "Size mismatch"); - cuopt_assert(problem_ptr->n_variables == problem_ptr->variable_lower_bounds.size(), - "Size mismatch"); - cuopt_assert(problem_ptr->n_variables == problem_ptr->variable_upper_bounds.size(), - "Size mismatch"); + cuopt_assert(problem_ptr->n_variables == problem_ptr->variable_bounds.size(), "Size mismatch"); return thrust::transform_reduce( handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(0) + problem_ptr->n_variables, cuda::proclaim_return_type([v = view()] __device__(i_t idx) -> f_t { - f_t lower_vio = max(0., v.problem.variable_lower_bounds[idx] - v.assignment[idx]); - f_t upper_vio = max(0., v.assignment[idx] - v.problem.variable_upper_bounds[idx]); + auto var_bounds = v.problem.variable_bounds[idx]; + f_t lower_vio = max(0., var_bounds.x - v.assignment[idx]); + f_t upper_vio = max(0., v.assignment[idx] - var_bounds.y); return max(lower_vio, upper_vio); }), 0., diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 841770c4db..3034ef95fe 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -88,9 +88,8 @@ mip_solution_t run_mip(detail::problem_t& problem, thrust::make_counting_iterator(0), thrust::make_counting_iterator(problem.n_variables), [sol = solution.assignment.data(), pb = problem.view()] __device__(i_t index) { - sol[index] = pb.objective_coefficients[index] > 0 - ? pb.variable_lower_bounds[index] - : pb.variable_upper_bounds[index]; + auto bounds = pb.variable_bounds[index]; + sol[index] = pb.objective_coefficients[index] > 0 ? bounds.x : bounds.y; }); problem.post_process_solution(solution); solution.compute_objective(); // just to ensure h_user_obj is set diff --git a/cpp/src/mip/utils.cuh b/cpp/src/mip/utils.cuh index 18759737d5..43872861f3 100644 --- a/cpp/src/mip/utils.cuh +++ b/cpp/src/mip/utils.cuh @@ -239,18 +239,18 @@ void clamp_within_var_bounds(rmm::device_uvector& assignment, { cuopt_assert(assignment.size() == problem_ptr->n_variables, "Size mismatch!"); f_t* assignment_ptr = assignment.data(); - thrust::for_each(handle_ptr->get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(0) + problem_ptr->n_variables, - [assignment_ptr, - lower_bound = problem_ptr->variable_lower_bounds.data(), - upper_bound = problem_ptr->variable_upper_bounds.data()] __device__(i_t idx) { - if (assignment_ptr[idx] < lower_bound[idx]) { - assignment_ptr[idx] = lower_bound[idx]; - } else if (assignment_ptr[idx] > upper_bound[idx]) { - assignment_ptr[idx] = upper_bound[idx]; - } - }); + thrust::for_each( + handle_ptr->get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(0) + problem_ptr->n_variables, + [assignment_ptr, variable_bound = problem_ptr->variable_bounds.data()] __device__(i_t idx) { + auto bound = variable_bound[idx]; + if (assignment_ptr[idx] < bound.x) { + assignment_ptr[idx] = bound.x; + } else if (assignment_ptr[idx] > bound.y) { + assignment_ptr[idx] = bound.y; + } + }); } template diff --git a/cpp/src/utilities/copy_helpers.hpp b/cpp/src/utilities/copy_helpers.hpp index 70fabc0abc..d491e29ae1 100644 --- a/cpp/src/utilities/copy_helpers.hpp +++ b/cpp/src/utilities/copy_helpers.hpp @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include @@ -48,6 +49,26 @@ struct type_2 { using type = double2; }; +template +struct scalar_type { + using type = void; +}; + +template <> +struct scalar_type { + using type = int; +}; + +template <> +struct scalar_type { + using type = float; +}; + +template <> +struct scalar_type { + using type = double; +}; + template raft::device_span::type> make_span_2(rmm::device_uvector& container) { @@ -276,4 +297,20 @@ inline void expand_device_copy(rmm::device_uvector& dst_vec, raft::copy(dst_vec.data(), src_vec.data(), src_vec.size(), stream_view); } +template +std::tuple, std::vector> extract_host_bounds( + const rmm::device_uvector& variable_bounds, const raft::handle_t* handle_ptr) +{ + rmm::device_uvector var_lb(variable_bounds.size(), handle_ptr->get_stream()); + rmm::device_uvector var_ub(variable_bounds.size(), handle_ptr->get_stream()); + thrust::transform(handle_ptr->get_thrust_policy(), + variable_bounds.begin(), + variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(var_lb.begin(), var_ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); + auto h_var_lb = cuopt::host_copy(var_lb); + auto h_var_ub = cuopt::host_copy(var_ub); + return std::make_tuple(h_var_lb, h_var_ub); +} + } // namespace cuopt diff --git a/cpp/tests/mip/load_balancing_test.cu b/cpp/tests/mip/load_balancing_test.cu index deed9ea85a..fb0d8b6e86 100644 --- a/cpp/tests/mip/load_balancing_test.cu +++ b/cpp/tests/mip/load_balancing_test.cu @@ -59,15 +59,14 @@ std::tuple, std::vector, std::vector> select_k_ auto seed = std::random_device{}(); std::cerr << "Tested with seed " << seed << "\n"; problem.compute_n_integer_vars(); - auto v_lb = host_copy(problem.variable_lower_bounds); - auto v_ub = host_copy(problem.variable_upper_bounds); + auto v_bnd = host_copy(problem.variable_bounds); auto int_var_id = host_copy(problem.integer_indices); - int_var_id.erase(std::remove_if(int_var_id.begin(), - int_var_id.end(), - [v_lb, v_ub](auto id) { - return !(std::isfinite(v_lb[id]) && std::isfinite(v_ub[id])); - }), - int_var_id.end()); + int_var_id.erase( + std::remove_if( + int_var_id.begin(), + int_var_id.end(), + [v_bnd](auto id) { return !(std::isfinite(v_bnd[id].x) && std::isfinite(v_bnd[id].y)); }), + int_var_id.end()); sample_size = std::min(sample_size, static_cast(int_var_id.size())); std::vector random_int_vars; std::mt19937 m{seed}; @@ -77,11 +76,11 @@ std::tuple, std::vector, std::vector> select_k_ std::vector probe_1(sample_size); for (int i = 0; i < static_cast(random_int_vars.size()); ++i) { if (i % 2) { - probe_0[i] = v_lb[random_int_vars[i]]; - probe_1[i] = v_ub[random_int_vars[i]]; + probe_0[i] = v_bnd[random_int_vars[i]].x; + probe_1[i] = v_bnd[random_int_vars[i]].y; } else { - probe_1[i] = v_lb[random_int_vars[i]]; - probe_0[i] = v_ub[random_int_vars[i]]; + probe_1[i] = v_bnd[random_int_vars[i]].x; + probe_0[i] = v_bnd[random_int_vars[i]].y; } } return std::make_tuple(std::move(random_int_vars), std::move(probe_0), std::move(probe_1)); From 8df30a64f1027e72a2c64a07cce38534a1808bc1 Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Wed, 3 Sep 2025 12:01:13 -0400 Subject: [PATCH 03/10] in progress --- cpp/src/mip/CMakeLists.txt | 5 - cpp/src/mip/diversity/diversity_manager.cu | 5 - .../feasibility_jump_kernels.cu | 64 +++++++------ .../feasibility_pump/feasibility_pump.cu | 6 +- .../feasibility_pump/feasibility_pump.cuh | 3 - cpp/src/mip/local_search/local_search.cu | 2 - cpp/src/mip/local_search/local_search.cuh | 1 - .../local_search/rounding/constraint_prop.cu | 69 +++++++------ .../local_search/rounding/constraint_prop.cuh | 6 +- cpp/src/mip/presolve/bounds_presolve.cu | 67 ++++++------- cpp/src/mip/presolve/bounds_presolve.cuh | 6 +- cpp/src/mip/presolve/multi_probe.cu | 54 +++++++---- cpp/src/mip/presolve/multi_probe.cuh | 6 +- cpp/src/mip/presolve/probing_cache.cuh | 13 --- cpp/src/mip/presolve/trivial_presolve.cuh | 64 +++++-------- .../mip/presolve/trivial_presolve_helpers.cuh | 63 ++++++------ cpp/src/mip/problem/problem.cu | 96 +++++++++---------- cpp/src/mip/problem/problem.cuh | 8 +- cpp/src/mip/problem/problem_helpers.cuh | 22 +++-- cpp/src/mip/problem/write_mps.cu | 3 +- cpp/tests/mip/CMakeLists.txt | 3 - cpp/tests/mip/elim_var_remap_test.cu | 16 ++-- cpp/tests/mip/multi_probe_test.cu | 9 +- 23 files changed, 273 insertions(+), 318 deletions(-) diff --git a/cpp/src/mip/CMakeLists.txt b/cpp/src/mip/CMakeLists.txt index 25c9a18bf3..09305df481 100644 --- a/cpp/src/mip/CMakeLists.txt +++ b/cpp/src/mip/CMakeLists.txt @@ -30,20 +30,15 @@ list(PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/local_search/local_search.cu ${CMAKE_CURRENT_SOURCE_DIR}/local_search/rounding/bounds_repair.cu ${CMAKE_CURRENT_SOURCE_DIR}/local_search/rounding/constraint_prop.cu - ${CMAKE_CURRENT_SOURCE_DIR}/local_search/rounding/lb_bounds_repair.cu - ${CMAKE_CURRENT_SOURCE_DIR}/local_search/rounding/lb_constraint_prop.cu ${CMAKE_CURRENT_SOURCE_DIR}/local_search/rounding/simple_rounding.cu ${CMAKE_CURRENT_SOURCE_DIR}/local_search/feasibility_pump/feasibility_pump.cu ${CMAKE_CURRENT_SOURCE_DIR}/local_search/line_segment_search/line_segment_search.cu ${CMAKE_CURRENT_SOURCE_DIR}/presolve/bounds_presolve.cu ${CMAKE_CURRENT_SOURCE_DIR}/presolve/bounds_update_data.cu ${CMAKE_CURRENT_SOURCE_DIR}/presolve/conditional_bound_strengthening.cu - ${CMAKE_CURRENT_SOURCE_DIR}/presolve/lb_probing_cache.cu - ${CMAKE_CURRENT_SOURCE_DIR}/presolve/load_balanced_bounds_presolve.cu ${CMAKE_CURRENT_SOURCE_DIR}/presolve/multi_probe.cu ${CMAKE_CURRENT_SOURCE_DIR}/presolve/probing_cache.cu ${CMAKE_CURRENT_SOURCE_DIR}/presolve/trivial_presolve.cu - ${CMAKE_CURRENT_SOURCE_DIR}/problem/load_balanced_problem.cu ${CMAKE_CURRENT_SOURCE_DIR}/feasibility_jump/feasibility_jump.cu ${CMAKE_CURRENT_SOURCE_DIR}/feasibility_jump/feasibility_jump_kernels.cu ${CMAKE_CURRENT_SOURCE_DIR}/solution/solution.cu) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index f7476a5eda..aa4aa1857c 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -396,8 +396,6 @@ solution_t diversity_manager_t::run_solver() // after every change to the problem, we should resize all the relevant vars // we need to encapsulate that to prevent repetitions ls.resize_vectors(*problem_ptr, problem_ptr->handle_ptr); - ls.lb_constraint_prop.temp_problem.setup(*problem_ptr); - ls.lb_constraint_prop.bounds_update.setup(ls.lb_constraint_prop.temp_problem); ls.constraint_prop.bounds_update.resize(*problem_ptr); problem_ptr->check_problem_representation(true); // have the structure ready for reusing later @@ -421,9 +419,6 @@ solution_t diversity_manager_t::run_solver() if (!fj_only_run) { compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); } - // careful, assign the correct probing cache - ls.lb_constraint_prop.bounds_update.probing_cache.probing_cache = - ls.constraint_prop.bounds_update.probing_cache.probing_cache; if (check_b_b_preemption()) { return population.best_feasible(); } lp_state_t& lp_state = problem_ptr->lp_state; diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 213d31bc5a..575144ee7f 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -438,16 +438,15 @@ DI std::pair::move_score_info_t> compute_best_mtm( auto best_score_info = fj_t::move_score_info_t::invalid(); // fixed variables - if (fj.pb.integer_equal(fj.pb.variable_lower_bounds[var_idx], - fj.pb.variable_upper_bounds[var_idx])) { - return std::make_pair(fj.pb.variable_lower_bounds[var_idx], - fj_t::move_score_info_t::invalid()); + auto bounds = fj.pb.variable_bounds[var_idx]; + if (fj.pb.integer_equal(bounds.x, bounds.y)) { + return std::make_pair(bounds.x, fj_t::move_score_info_t::invalid()); } f_t old_val = fj.incumbent_assignment[var_idx]; f_t obj_coeff = fj.pb.objective_coefficients[var_idx]; - f_t v_lb = fj.pb.variable_lower_bounds[var_idx]; - f_t v_ub = fj.pb.variable_upper_bounds[var_idx]; + f_t v_lb = bounds.x; + f_t v_ub = bounds.y; raft::random::PCGenerator rng(fj.settings->seed + *fj.iterations, 0, 0); cuopt_assert(isfinite(v_lb) || isfinite(v_ub), "unexpected free variable"); @@ -549,9 +548,8 @@ DI void update_jump_value(typename fj_t::climber_data_t::view_t fj, i_ cuopt_assert(fj.pb.integer_equal(fj.incumbent_assignment[var_idx], 0) || fj.pb.integer_equal(fj.incumbent_assignment[var_idx], 1), "Current assignment is not binary!"); - cuopt_assert( - fj.pb.variable_lower_bounds[var_idx] == 0 && fj.pb.variable_upper_bounds[var_idx] == 1, - ""); + cuopt_assert(fj.pb.variable_bounds[var_idx].x == 0 && fj.pb.variable_bounds[var_idx].y == 1, + ""); cuopt_assert( fj.pb.check_variable_within_bounds(var_idx, fj.incumbent_assignment[var_idx] + delta), "Var not within bounds!"); @@ -566,8 +564,8 @@ DI void update_jump_value(typename fj_t::climber_data_t::view_t fj, i_ } else { delta = round(1.0 - 2 * fj.incumbent_assignment[var_idx]); if (threadIdx.x == 0) { - cuopt_assert( - fj.pb.variable_lower_bounds[var_idx] == 0 && fj.pb.variable_upper_bounds[var_idx] == 1, ""); + cuopt_assert(fj.pb.variable_bounds[var_idx].x == 0 && fj.pb.variable_bounds[var_idx].y == 1, + ""); cuopt_assert( fj.pb.check_variable_within_bounds(var_idx, fj.incumbent_assignment[var_idx] + delta), "Var not within bounds!"); @@ -795,7 +793,8 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t } } - i_t var_range = fj.pb.variable_upper_bounds[var_idx] - fj.pb.variable_lower_bounds[var_idx]; + auto bounds = fj.pb.variable_bounds[var_idx]; + i_t var_range = bounds.y - bounds.x; double delta_rel_err = fabs(fj.jump_move_delta[var_idx]) / var_range; if (delta_rel_err < fj.settings->parameters.small_move_tabu_threshold) { *fj.small_move_tabu = *fj.iterations; @@ -807,8 +806,8 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t "err_range %.2g%%, infeas %.2g, total viol %d\n", *fj.iterations, var_idx, - fj.pb.variable_lower_bounds[var_idx], - fj.pb.variable_upper_bounds[var_idx], + fj.pb.variable_bounds[var_idx].x, + fj.pb.variable_bounds[var_idx].y, fj.incumbent_assignment[var_idx], fj.jump_move_delta[var_idx], fj.incumbent_assignment[var_idx] + fj.jump_move_delta[var_idx], @@ -891,8 +890,9 @@ DI void update_lift_moves(typename fj_t::climber_data_t::view_t fj) f_t obj_coeff = fj.pb.objective_coefficients[var_idx]; f_t delta = -std::numeric_limits::infinity(); - f_t th_lower_delta = fj.pb.variable_lower_bounds[var_idx] - fj.incumbent_assignment[var_idx]; - f_t th_upper_delta = fj.pb.variable_upper_bounds[var_idx] - fj.incumbent_assignment[var_idx]; + auto bounds = fj.pb.variable_bounds[var_idx]; + f_t th_lower_delta = bounds.x - fj.incumbent_assignment[var_idx]; + f_t th_upper_delta = bounds.y - fj.incumbent_assignment[var_idx]; auto [offset_begin, offset_end] = fj.pb.reverse_range_for_var(var_idx); for (i_t j = threadIdx.x + offset_begin; j < offset_end; j += blockDim.x) { auto cstr_idx = fj.pb.reverse_constraints[j]; @@ -992,8 +992,9 @@ template DI f_t get_breakthrough_move(typename fj_t::climber_data_t::view_t fj, i_t var_idx) { f_t obj_coeff = fj.pb.objective_coefficients[var_idx]; - f_t v_lb = fj.pb.variable_lower_bounds[var_idx]; - f_t v_ub = fj.pb.variable_upper_bounds[var_idx]; + auto bounds = fj.pb.variable_bounds[var_idx]; + f_t v_lb = bounds.x; + f_t v_ub = bounds.y; cuopt_assert(isfinite(v_lb) || isfinite(v_ub), "unexpected free variable"); cuopt_assert(v_lb <= v_ub, "invalid bounds"); cuopt_assert(fj.pb.check_variable_within_bounds(var_idx, fj.incumbent_assignment[var_idx]), @@ -1217,8 +1218,8 @@ __device__ void compute_mtm_moves(typename fj_t::climber_data_t::view_ bool exclude_from_search = false; // "fixed" variables are to be excluded (as they cannot take any other value) - exclude_from_search |= fj.pb.integer_equal(fj.pb.variable_lower_bounds[var_idx], - fj.pb.variable_upper_bounds[var_idx]); + auto bounds = fj.pb.variable_bounds[var_idx]; + exclude_from_search |= fj.pb.integer_equal(bounds.x, bounds.y); if (exclude_from_search) { if (threadIdx.x == 0) { @@ -1272,7 +1273,8 @@ __global__ void select_variable_kernel(typename fj_t::climber_data_t:: auto var_idx = fj.candidate_variables.contents[setidx]; auto move_score = fj.jump_move_scores[var_idx]; - i_t var_range = fj.pb.variable_upper_bounds[var_idx] - fj.pb.variable_lower_bounds[var_idx]; + auto bounds = fj.pb.variable_bounds[var_idx]; + i_t var_range = bounds.y - bounds.x; double delta_rel_err = fabs(fj.jump_move_delta[var_idx]) / var_range; // tabu for small moves to avoid very long descents/numerical issues if (delta_rel_err < fj.settings->parameters.small_move_tabu_threshold && @@ -1319,16 +1321,16 @@ __global__ void select_variable_kernel(typename fj_t::climber_data_t:: *fj.selected_var = selected_var; if (selected_var != std::numeric_limits::max()) { #if FJ_SINGLE_STEP - i_t var_range = - fj.pb.variable_upper_bounds[selected_var] - fj.pb.variable_lower_bounds[selected_var]; + auto bounds = fj.pb.variable_bounds[selected_var]; + i_t var_range = bounds.y - bounds.x; double delta_rel_err = fabs(fj.jump_move_delta[selected_var]) / var_range * 100; DEVICE_LOG_INFO( "=---- FJ: selected %d [%g/%g] %c :%.4g+{%.4g}=%.4g score {%g,%g}, d_obj %.2g+%.2g->%.2g, " "delta_rel_err %.2g%%, " "infeas %.2g, total viol %d, out of %d\n", selected_var, - fj.pb.variable_lower_bounds[selected_var], - fj.pb.variable_upper_bounds[selected_var], + bounds.x, + bounds.y, fj.pb.variable_types[selected_var] == var_t::INTEGER ? 'I' : 'C', fj.incumbent_assignment[selected_var], fj.jump_move_delta[selected_var], @@ -1553,9 +1555,10 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat // if no move was found, fallback to round-nearest if (fj.pb.integer_equal(delta, 0)) { - delta = round_nearest(fj.incumbent_assignment[selected], - fj.pb.variable_lower_bounds[selected], - fj.pb.variable_upper_bounds[selected], + auto bounds = fj.pb.variable_bounds[selected]; + delta = round_nearest(fj.incumbent_assignment[selected], + bounds.x, + bounds.y, fj.pb.tolerances.integrality_tolerance, rng) - fj.incumbent_assignment[selected]; @@ -1564,10 +1567,11 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat if (FIRST_THREAD) { fj.jump_move_delta[selected] = delta; *fj.selected_var = selected; + auto bounds = fj.pb.variable_bounds[*fj.selected_var]; DEVICE_LOG_TRACE("selected_var: %d bounds [%.4g/%.4g], delta %g, old val %g\n", *fj.selected_var, - fj.pb.variable_lower_bounds[*fj.selected_var], - fj.pb.variable_upper_bounds[*fj.selected_var], + bounds.x, + bounds.y, fj.jump_move_delta[*fj.selected_var], fj.incumbent_assignment[*fj.selected_var]); } diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index 5a7c157133..e38edf4d30 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -46,7 +46,6 @@ feasibility_pump_t::feasibility_pump_t( fj_t& fj_, // fj_tree_t& fj_tree_, constraint_prop_t& constraint_prop_, - lb_constraint_prop_t& lb_constraint_prop_, line_segment_search_t& line_segment_search_, rmm::device_uvector& lp_optimal_solution_) : context(context_), @@ -55,7 +54,6 @@ feasibility_pump_t::feasibility_pump_t( line_segment_search(line_segment_search_), cycle_queue(*context.problem_ptr), constraint_prop(constraint_prop_), - lb_constraint_prop(lb_constraint_prop_), last_rounding(context.problem_ptr->n_variables, context.problem_ptr->handle_ptr->get_stream()), last_projection(context.problem_ptr->n_variables, context.problem_ptr->handle_ptr->get_stream()), @@ -437,9 +435,7 @@ void feasibility_pump_t::relax_general_integers(solution_t& { orig_variable_types.resize(solution.problem_ptr->n_variables, solution.handle_ptr->get_stream()); - auto var_types = make_span(solution.problem_ptr->variable_types); - // auto var_lb = make_span(solution.problem_ptr->variable_lower_bounds); - // auto var_ub = make_span(solution.problem_ptr->variable_upper_bounds); + auto var_types = make_span(solution.problem_ptr->variable_types); auto var_bnds = make_span(solution.problem_ptr->variable_bounds); auto copy_types = make_span(orig_variable_types); diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh index 8b7891b04b..727a929f01 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -118,7 +117,6 @@ class feasibility_pump_t { fj_t& fj, // fj_tree_t& fj_tree_, constraint_prop_t& constraint_prop_, - lb_constraint_prop_t& lb_constraint_prop_, line_segment_search_t& line_segment_search_, rmm::device_uvector& lp_optimal_solution_); @@ -153,7 +151,6 @@ class feasibility_pump_t { line_segment_search_t& line_segment_search; cycle_queue_t cycle_queue; constraint_prop_t& constraint_prop; - lb_constraint_prop_t& lb_constraint_prop; fp_config_t config; rmm::device_uvector last_rounding; rmm::device_uvector last_projection; diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 711612550b..a2eda273b3 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -40,13 +40,11 @@ local_search_t::local_search_t(mip_solver_context_t& context fj(context), // fj_tree(fj), constraint_prop(context), - lb_constraint_prop(context), line_segment_search(fj, constraint_prop), fp(context, fj, // fj_tree, constraint_prop, - lb_constraint_prop, line_segment_search, lp_optimal_solution_), rng(cuopt::seed_generator::get_seed()) diff --git a/cpp/src/mip/local_search/local_search.cuh b/cpp/src/mip/local_search/local_search.cuh index fdb3ff5803..90b4ed7b21 100644 --- a/cpp/src/mip/local_search/local_search.cuh +++ b/cpp/src/mip/local_search/local_search.cuh @@ -76,7 +76,6 @@ class local_search_t { fj_t fj; // fj_tree_t fj_tree; constraint_prop_t constraint_prop; - lb_constraint_prop_t lb_constraint_prop; line_segment_search_t line_segment_search; feasibility_pump_t fp; std::mt19937 rng; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 50fed7b801..a413af0e38 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -430,8 +430,7 @@ void constraint_prop_t::collapse_crossing_bounds(problem_t& cuopt_assert(o_lb - int_tol <= val_to_collapse && val_to_collapse <= o_ub + int_tol, "Out of original bounds!"); - using f_t2 = typename type_2::type; - v_bnds[idx] = f_t2{val_to_collapse, val_to_collapse}; + v_bnds[idx] = typename type_2::type{val_to_collapse, val_to_collapse}; } }); } @@ -447,10 +446,9 @@ void constraint_prop_t::set_bounds_on_fixed_vars(solution_t& sol.problem_ptr->integer_indices.begin(), sol.problem_ptr->integer_indices.end(), [pb = sol.problem_ptr->view(), assgn, var_bounds] __device__(i_t idx) { - using f_t2 = typename type_2::type; auto var_val = assgn[idx]; if (pb.is_integer(var_val)) { - var_bounds[idx] = f_t2{var_val, var_val}; + var_bounds[idx] = typename type_2::type{var_val, var_val}; // lb[idx] = var_val; // ub[idx] = var_val; } @@ -461,8 +459,8 @@ template struct is_bound_fixed_t { // This functor should be called only on integer variables f_t eps; - raft::device_span bnd; - raft::device_span original_bnd; + raft::device_span::type> bnd; + raft::device_span::type> original_bnd; raft::device_span assignment; is_bound_fixed_t(f_t eps_, raft::device_span bnd_, @@ -523,26 +521,28 @@ struct greater_than_threshold_t { }; template -template -void constraint_prop_t::copy_bounds(rmm::device_uvector& output_bounds, - const rmm::device_uvector& input_lb, - const rmm::device_uvector& input_ub, - const raft::handle_t* handle_ptr) +void constraint_prop_t::copy_bounds( + rmm::device_uvector::type>& output_bounds, + const rmm::device_uvector& input_lb, + const rmm::device_uvector& input_ub, + const raft::handle_t* handle_ptr) { thrust::transform( handle_ptr->get_thrust_policy(), thrust::make_zip_iterator(thrust::make_tuple(input_lb.begin(), input_ub.begin())), thrust::make_zip_iterator(thrust::make_tuple(input_lb.end(), input_ub.end())), output_bounds.begin(), - [] __device__(auto bounds) { return f_t2{thrust::get<0>(bounds), thrust::get<1>(bounds)}; }); + [] __device__(auto bounds) { + return typename type_2::type{thrust::get<0>(bounds), thrust::get<1>(bounds)}; + }); } template -template -void constraint_prop_t::copy_bounds(rmm::device_uvector& output_lb, - rmm::device_uvector& output_ub, - const rmm::device_uvector& input_bounds, - const raft::handle_t* handle_ptr) +void constraint_prop_t::copy_bounds( + rmm::device_uvector& output_lb, + rmm::device_uvector& output_ub, + const rmm::device_uvector::type>& input_bounds, + const raft::handle_t* handle_ptr) { thrust::transform( handle_ptr->get_thrust_policy(), @@ -1043,15 +1043,15 @@ bool constraint_prop_t::find_integer( rounding_ii = false; n_iter_in_recovery = 0; // during repair procedure some variables might be collapsed - using f_t2 = typename type_2::type; - auto iter = thrust::stable_partition( - sol.handle_ptr->get_thrust_policy(), - unset_vars.begin() + set_count, - unset_vars.end(), - is_bound_fixed_t{orig_sol.problem_ptr->tolerances.integrality_tolerance, - make_span(sol.problem_ptr->variable_bounds), - make_span(orig_sol.problem_ptr->variable_bounds), - make_span(sol.assignment)}); + auto iter = + thrust::stable_partition(sol.handle_ptr->get_thrust_policy(), + unset_vars.begin() + set_count, + unset_vars.end(), + is_bound_fixed_t::type>{ + orig_sol.problem_ptr->tolerances.integrality_tolerance, + make_span(sol.problem_ptr->variable_bounds), + make_span(orig_sol.problem_ptr->variable_bounds), + make_span(sol.assignment)}); i_t n_fixed_vars = (iter - (unset_vars.begin() + set_count)); CUOPT_LOG_TRACE("After repair procedure, number of additional fixed vars %d", n_fixed_vars); set_count += n_fixed_vars; @@ -1234,15 +1234,14 @@ bool constraint_prop_t::handle_fixed_vars( auto set_count = *set_count_ptr; const f_t int_tol = sol.problem_ptr->tolerances.integrality_tolerance; // which other variables were affected? - using f_t2 = typename type_2::type; - auto iter = thrust::stable_partition( - sol.handle_ptr->get_thrust_policy(), - unset_vars.begin() + set_count, - unset_vars.end(), - is_bound_fixed_t{int_tol, - make_span(sol.problem_ptr->variable_bounds), - make_span(original_problem->variable_bounds), - make_span(sol.assignment)}); + auto iter = thrust::stable_partition(sol.handle_ptr->get_thrust_policy(), + unset_vars.begin() + set_count, + unset_vars.end(), + is_bound_fixed_t::type>{ + int_tol, + make_span(sol.problem_ptr->variable_bounds), + make_span(original_problem->variable_bounds), + make_span(sol.assignment)}); i_t n_fixed_vars = (iter - (unset_vars.begin() + set_count)); cuopt_assert(n_fixed_vars >= std::get<0>(var_probe_vals).size(), "Error in number of vars fixed!"); diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cuh b/cpp/src/mip/local_search/rounding/constraint_prop.cuh index 451822519e..3b01da2749 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cuh +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cuh @@ -96,14 +96,12 @@ struct constraint_prop_t { void restore_bounds(solution_t& sol); void save_bounds(solution_t& sol); - template void copy_bounds(rmm::device_uvector& output_lb, rmm::device_uvector& output_ub, - const rmm::device_uvector& input_bounds, + const rmm::device_uvector::type>& input_bounds, const raft::handle_t* handle_ptr); - template - void copy_bounds(rmm::device_uvector& output_bounds, + void copy_bounds(rmm::device_uvector::type>& output_bounds, const rmm::device_uvector& input_lb, const rmm::device_uvector& input_ub, const raft::handle_t* handle_ptr); diff --git a/cpp/src/mip/presolve/bounds_presolve.cu b/cpp/src/mip/presolve/bounds_presolve.cu index 72440cd9a8..7ba86dd32d 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cu +++ b/cpp/src/mip/presolve/bounds_presolve.cu @@ -212,14 +212,7 @@ void bound_presolve_t::calculate_activity_on_problem_bounds(problem_t< { auto& handle_ptr = pb.handle_ptr; upd.init_changed_constraints(handle_ptr); - cuopt_assert(upd.lb.size() == pb.variable_lower_bounds.size(), - "size of variable lower bound mismatch"); - raft::copy( - upd.lb.data(), pb.variable_lower_bounds.data(), upd.lb.size(), handle_ptr->get_stream()); - cuopt_assert(upd.ub.size() == pb.variable_upper_bounds.size(), - "size of variable upper bound mismatch"); - raft::copy( - upd.ub.data(), pb.variable_upper_bounds.data(), upd.ub.size(), handle_ptr->get_stream()); + copy_input_bounds(pb); calculate_activity(pb); } @@ -228,14 +221,14 @@ void bound_presolve_t::copy_input_bounds(problem_t& pb) { auto& handle_ptr = pb.handle_ptr; - cuopt_assert(upd.lb.size() == pb.variable_lower_bounds.size(), - "size of variable lower bound mismatch"); - raft::copy( - upd.lb.data(), pb.variable_lower_bounds.data(), upd.lb.size(), handle_ptr->get_stream()); - cuopt_assert(upd.ub.size() == pb.variable_upper_bounds.size(), - "size of variable upper bound mismatch"); - raft::copy( - upd.ub.data(), pb.variable_upper_bounds.data(), upd.ub.size(), handle_ptr->get_stream()); + cuopt_assert(upd.lb.size() == pb.variable_bounds.size(), "size of variable lower bound mismatch"); + cuopt_assert(upd.ub.size() == pb.variable_bounds.size(), "size of variable upper bound mismatch"); + + thrust::transform(handle_ptr->get_thrust_policy(), + pb.variable_bounds.begin(), + pb.variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(upd.lb.begin(), upd.ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); } template @@ -271,30 +264,11 @@ termination_criterion_t bound_presolve_t::solve( } template -termination_criterion_t bound_presolve_t::solve(problem_t& pb, - raft::device_span input_lb, - raft::device_span input_ub) +termination_criterion_t bound_presolve_t::solve(problem_t& pb) { timer_t timer(settings.time_limit); auto& handle_ptr = pb.handle_ptr; - if (input_lb.size() == 0) { - cuopt_assert(upd.lb.size() == pb.variable_lower_bounds.size(), - "size of variable lower bound mismatch"); - raft::copy( - upd.lb.data(), pb.variable_lower_bounds.data(), upd.lb.size(), handle_ptr->get_stream()); - } else { - cuopt_assert(input_lb.size() == upd.lb.size(), "size of variable lower bound mismatch"); - raft::copy(upd.lb.data(), input_lb.data(), input_lb.size(), handle_ptr->get_stream()); - } - if (input_ub.size() == 0) { - cuopt_assert(upd.ub.size() == pb.variable_upper_bounds.size(), - "size of variable upper bound mismatch"); - raft::copy( - upd.ub.data(), pb.variable_upper_bounds.data(), upd.ub.size(), handle_ptr->get_stream()); - } else { - cuopt_assert(input_ub.size() == upd.ub.size(), "size of variable lower bound mismatch"); - raft::copy(upd.ub.data(), input_ub.data(), upd.ub.size(), handle_ptr->get_stream()); - } + copy_input_bounds(pb); return bound_update_loop(pb, timer); } @@ -329,9 +303,7 @@ bool bound_presolve_t::calculate_infeasible_redundant_constraints(prob template void bound_presolve_t::set_updated_bounds(problem_t& pb) { - set_updated_bounds(pb.handle_ptr, - cuopt::make_span(pb.variable_lower_bounds), - cuopt::make_span(pb.variable_upper_bounds)); + set_updated_bounds(pb.handle_ptr, cuopt::make_span(pb.variable_bounds)); pb.compute_n_integer_vars(); pb.compute_binary_var_table(); } @@ -347,6 +319,21 @@ void bound_presolve_t::set_updated_bounds(const raft::handle_t* handle raft::copy(output_ub.data(), upd.ub.data(), upd.ub.size(), handle_ptr->get_stream()); } +template +void bound_presolve_t::set_updated_bounds( + const raft::handle_t* handle_ptr, raft::device_span::type> output_bounds) +{ + cuopt_assert(ub.size() == output_bounds.size(), "size of variable upper bound mismatch"); + cuopt_assert(lb.size() == output_bounds.size(), "size of variable lower bound mismatch"); + thrust::transform(handle_ptr->get_thrust_policy(), + thrust::make_zip_iterator(thrust::make_tuple(upd.lb.begin(), upd.ub.begin())), + thrust::make_zip_iterator(thrust::make_tuple(upd.lb.end(), upd.ub.end())), + output_bounds.begin(), + [] __device__(auto i) { + return typename type_2::type{thrust::get<0>(i), thrust::get<1>(i)}; + }); +} + template void bound_presolve_t::calc_and_set_updated_constraint_bounds(problem_t& pb) { diff --git a/cpp/src/mip/presolve/bounds_presolve.cuh b/cpp/src/mip/presolve/bounds_presolve.cuh index 84853a7812..0cb818e8ca 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cuh +++ b/cpp/src/mip/presolve/bounds_presolve.cuh @@ -50,9 +50,7 @@ class bound_presolve_t { // when we need to accept a vector, we can use input_lb version termination_criterion_t solve(problem_t& pb, f_t lb, f_t ub, i_t var_idx); - termination_criterion_t solve(problem_t& pb, - raft::device_span input_lb = {}, - raft::device_span input_ub = {}); + termination_criterion_t solve(problem_t& pb); termination_criterion_t solve(problem_t& pb, const std::vector>& var_probe_val_pairs, @@ -62,6 +60,8 @@ class bound_presolve_t { void calculate_activity_on_problem_bounds(problem_t& pb); bool calculate_bounds_update(problem_t& pb); void set_updated_bounds(problem_t& pb); + void set_updated_bounds(const raft::handle_t* handle_ptr, + raft::device_span::type> output_bounds); void set_updated_bounds(const raft::handle_t* handle_ptr, raft::device_span output_lb, raft::device_span output_ub); diff --git a/cpp/src/mip/presolve/multi_probe.cu b/cpp/src/mip/presolve/multi_probe.cu index 0d972baae2..58b59c19a3 100644 --- a/cpp/src/mip/presolve/multi_probe.cu +++ b/cpp/src/mip/presolve/multi_probe.cu @@ -331,9 +331,9 @@ void multi_probe_t::update_device_bounds(const raft::handle_t* handle_ } template -template -void multi_probe_t::update_host_bounds(const raft::handle_t* handle_ptr, - const raft::device_span variable_bounds) +void multi_probe_t::update_host_bounds( + const raft::handle_t* handle_ptr, + const raft::device_span::type> variable_bounds) { cuopt_assert(variable_bounds.size() == host_lb.size(), "size of variable lower bound mismatch"); // raft::copy(host_lb.data(), variable_lb.data(), variable_lb.size(), handle_ptr->get_stream()); @@ -355,22 +355,21 @@ template void multi_probe_t::copy_problem_into_probing_buffers(problem_t& pb, const raft::handle_t* handle_ptr) { - cuopt_assert(upd_0.lb.size() == pb.variable_lower_bounds.size(), + cuopt_assert(upd_0.lb.size() == pb.variable_bounds.size(), "size of variable lower bound mismatch"); - raft::copy( - upd_0.lb.data(), pb.variable_lower_bounds.data(), upd_0.lb.size(), handle_ptr->get_stream()); - cuopt_assert(upd_1.lb.size() == pb.variable_lower_bounds.size(), + cuopt_assert(upd_1.lb.size() == pb.variable_bounds.size(), "size of variable lower bound mismatch"); - raft::copy( - upd_1.lb.data(), pb.variable_lower_bounds.data(), upd_1.lb.size(), handle_ptr->get_stream()); - cuopt_assert(upd_0.ub.size() == pb.variable_upper_bounds.size(), + cuopt_assert(upd_0.ub.size() == pb.variable_bounds.size(), "size of variable upper bound mismatch"); - raft::copy( - upd_0.ub.data(), pb.variable_upper_bounds.data(), upd_0.ub.size(), handle_ptr->get_stream()); - cuopt_assert(upd_1.ub.size() == pb.variable_upper_bounds.size(), + cuopt_assert(upd_1.ub.size() == pb.variable_bounds.size(), "size of variable upper bound mismatch"); - raft::copy( - upd_1.ub.data(), pb.variable_upper_bounds.data(), upd_1.ub.size(), handle_ptr->get_stream()); + + thrust::transform(handle_ptr->get_thrust_policy(), + pb.variable_bounds.begin(), + pb.variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple( + upd_0.lb.begin(), upd_0.ub.begin(), upd_1.lb.begin(), upd_1.ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(i.x, i.y, i.x, i.y); }); } template @@ -420,6 +419,26 @@ void multi_probe_t::set_updated_bounds(const raft::handle_t* handle_pt raft::copy(output_lb.data(), lb.data(), lb.size(), handle_ptr->get_stream()); } +template +void multi_probe_t::set_updated_bounds( + const raft::handle_t* handle_ptr, + raft::device_span::type> output_bounds, + i_t select_update) +{ + auto& lb = select_update ? upd_1.lb : upd_0.lb; + auto& ub = select_update ? upd_1.ub : upd_0.ub; + + cuopt_assert(ub.size() == output_bounds.size(), "size of variable upper bound mismatch"); + cuopt_assert(lb.size() == output_bounds.size(), "size of variable lower bound mismatch"); + thrust::transform(handle_ptr->get_thrust_policy(), + thrust::make_zip_iterator(thrust::make_tuple(lb.begin(), ub.begin())), + thrust::make_zip_iterator(thrust::make_tuple(lb.end(), ub.end())), + output_bounds.begin(), + [] __device__(auto i) { + return typename type_2::type{thrust::get<0>(i), thrust::get<1>(i)}; + }); +} + template void multi_probe_t::constraint_stats(problem_t& pb, const raft::handle_t* handle_ptr) @@ -464,10 +483,7 @@ void multi_probe_t::set_updated_bounds(problem_t& pb, i_t select_update, const raft::handle_t* handle_ptr) { - set_updated_bounds(handle_ptr, - make_span(pb.variable_lower_bounds), - make_span(pb.variable_upper_bounds), - select_update); + set_updated_bounds(handle_ptr, make_span(pb.variable_bounds), select_update); } #if MIP_INSTANTIATE_FLOAT diff --git a/cpp/src/mip/presolve/multi_probe.cuh b/cpp/src/mip/presolve/multi_probe.cuh index 61e710cd6d..db0b59f4ba 100644 --- a/cpp/src/mip/presolve/multi_probe.cuh +++ b/cpp/src/mip/presolve/multi_probe.cuh @@ -55,6 +55,9 @@ class multi_probe_t { void set_updated_bounds(problem_t& pb, i_t select_update, const raft::handle_t* handle_ptr); + void set_updated_bounds(const raft::handle_t* handle_ptr, + raft::device_span::type> output_bounds, + i_t select_update); void set_updated_bounds(const raft::handle_t* handle_ptr, raft::device_span output_lb, raft::device_span output_ub, @@ -71,9 +74,8 @@ class multi_probe_t { const raft::handle_t* handle_ptr); void constraint_stats(problem_t& pb, const raft::handle_t* handle_ptr); void copy_problem_into_probing_buffers(problem_t& pb, const raft::handle_t* handle_ptr); - template void update_host_bounds(const raft::handle_t* handle_ptr, - const raft::device_span variable_bounds); + const raft::device_span::type> variable_bounds); void update_device_bounds(const raft::handle_t* handle_ptr); mip_solver_context_t& context; bounds_update_data_t upd_0; diff --git a/cpp/src/mip/presolve/probing_cache.cuh b/cpp/src/mip/presolve/probing_cache.cuh index 2ba5010c6b..755c18b0bb 100644 --- a/cpp/src/mip/presolve/probing_cache.cuh +++ b/cpp/src/mip/presolve/probing_cache.cuh @@ -17,8 +17,6 @@ #pragma once -#include -#include #include "bounds_presolve.cuh" #include @@ -30,12 +28,6 @@ namespace cuopt::linear_programming::detail { template class bound_presolve_t; -template -class load_balanced_bounds_presolve_t; - -template -class load_balanced_problem_t; - /* Probing cache is a set of implied bounds when we set a variable to some value. We keep two sets of changed bounds for each interval: @@ -137,9 +129,4 @@ void compute_probing_cache(bound_presolve_t& bound_presolve, problem_t& problem, timer_t timer); -template -void compute_probing_cache(load_balanced_bounds_presolve_t& bound_presolve, - load_balanced_problem_t& problem, - timer_t timer); - } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/presolve/trivial_presolve.cuh b/cpp/src/mip/presolve/trivial_presolve.cuh index f4940a62f6..803bfca800 100644 --- a/cpp/src/mip/presolve/trivial_presolve.cuh +++ b/cpp/src/mip/presolve/trivial_presolve.cuh @@ -77,16 +77,11 @@ void cleanup_vectors(problem_t& pb, handle_ptr->get_stream()); handle_ptr->sync_stream(); - auto lb_iter = thrust::remove_if(handle_ptr->get_thrust_policy(), - pb.variable_lower_bounds.begin(), - pb.variable_lower_bounds.end(), - var_map.begin(), - is_zero_t{}); - auto ub_iter = thrust::remove_if(handle_ptr->get_thrust_policy(), - pb.variable_upper_bounds.begin(), - pb.variable_upper_bounds.end(), - var_map.begin(), - is_zero_t{}); + auto bnd_iter = thrust::remove_if(handle_ptr->get_thrust_policy(), + pb.variable_bounds.begin(), + pb.variable_bounds.end(), + var_map.begin(), + is_zero_t{}); auto type_iter = thrust::remove_if(handle_ptr->get_thrust_policy(), pb.variable_types.begin(), pb.variable_types.end(), @@ -102,10 +97,7 @@ void cleanup_vectors(problem_t& pb, pb.objective_coefficients.end(), var_map.begin(), is_zero_t{}); - pb.variable_lower_bounds.resize(lb_iter - pb.variable_lower_bounds.begin(), - handle_ptr->get_stream()); - pb.variable_upper_bounds.resize(ub_iter - pb.variable_upper_bounds.begin(), - handle_ptr->get_stream()); + pb.variable_bounds.resize(bnd_iter - pb.variable_bounds.begin(), handle_ptr->get_stream()); pb.variable_types.resize(type_iter - pb.variable_types.begin(), handle_ptr->get_stream()); pb.is_binary_variable.resize(binary_iter - pb.is_binary_variable.begin(), handle_ptr->get_stream()); @@ -117,6 +109,7 @@ void cleanup_vectors(problem_t& pb, template void update_from_csr(problem_t& pb) { + using f_t2 = typename type_2::type; auto handle_ptr = pb.handle_ptr; rmm::device_uvector cnst(pb.coefficients.size(), handle_ptr->get_stream()); thrust::uninitialized_fill(handle_ptr->get_thrust_policy(), cnst.begin(), cnst.end(), 0); @@ -145,9 +138,8 @@ void update_from_csr(problem_t& pb) thrust::stable_partition(handle_ptr->get_thrust_policy(), coo_begin, coo_begin + cnst.size(), - is_variable_free_t{pb.tolerances.integrality_tolerance, - make_span(pb.variable_lower_bounds), - make_span(pb.variable_upper_bounds)}); + is_variable_free_t{pb.tolerances.integrality_tolerance, + make_span(pb.variable_bounds)}); RAFT_CHECK_CUDA(handle_ptr->get_stream()); nnz_edge_count = partition_iter - coo_begin; } @@ -180,12 +172,11 @@ void update_from_csr(problem_t& pb) handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(pb.n_variables), - assign_fixed_var_t{make_span(var_map), - make_span(pb.variable_lower_bounds), - make_span(pb.variable_upper_bounds), - make_span(pb.objective_coefficients), - make_span(pb.presolve_data.variable_mapping), - make_span(pb.presolve_data.fixed_var_assignment)}); + assign_fixed_var_t{make_span(var_map), + make_span(pb.variable_bounds), + make_span(pb.objective_coefficients), + make_span(pb.presolve_data.variable_mapping), + make_span(pb.presolve_data.fixed_var_assignment)}); auto used_iter = thrust::stable_partition(handle_ptr->get_thrust_policy(), pb.presolve_data.variable_mapping.begin(), pb.presolve_data.variable_mapping.end(), @@ -203,11 +194,10 @@ void update_from_csr(problem_t& pb) handle_ptr->get_stream()); rmm::device_uvector unused_coo_cnst_bound_updates(cnst.size() - nnz_edge_count, handle_ptr->get_stream()); - elem_multi_t mul{make_span(pb.coefficients), - make_span(pb.variables), - make_span(pb.objective_coefficients), - make_span(pb.variable_lower_bounds), - make_span(pb.variable_upper_bounds)}; + elem_multi_t mul{make_span(pb.coefficients), + make_span(pb.variables), + make_span(pb.objective_coefficients), + make_span(pb.variable_bounds)}; auto iter = thrust::reduce_by_key( handle_ptr->get_thrust_policy(), @@ -233,16 +223,14 @@ void update_from_csr(problem_t& pb) } // update objective_offset - pb.presolve_data.objective_offset += - thrust::transform_reduce(handle_ptr->get_thrust_policy(), - thrust::counting_iterator(0), - thrust::counting_iterator(pb.n_variables), - unused_var_obj_offset_t{make_span(var_map), - make_span(pb.objective_coefficients), - make_span(pb.variable_lower_bounds), - make_span(pb.variable_upper_bounds)}, - 0., - thrust::plus{}); + pb.presolve_data.objective_offset += thrust::transform_reduce( + handle_ptr->get_thrust_policy(), + thrust::counting_iterator(0), + thrust::counting_iterator(pb.n_variables), + unused_var_obj_offset_t{ + make_span(var_map), make_span(pb.objective_coefficients), make_span(pb.variable_bounds)}, + 0., + thrust::plus{}); RAFT_CHECK_CUDA(handle_ptr->get_stream()); // create renumbering maps diff --git a/cpp/src/mip/presolve/trivial_presolve_helpers.cuh b/cpp/src/mip/presolve/trivial_presolve_helpers.cuh index b7e3f4b464..22736a3113 100644 --- a/cpp/src/mip/presolve/trivial_presolve_helpers.cuh +++ b/cpp/src/mip/presolve/trivial_presolve_helpers.cuh @@ -30,40 +30,34 @@ struct non_zero_degree_t { __device__ i_t operator()(i_t i) { return offsets[i] != offsets[i + 1]; } }; -template +template struct is_variable_free_t { f_t tol; - raft::device_span lb; - raft::device_span ub; - is_variable_free_t(f_t tol_, raft::device_span lb_, raft::device_span ub_) - : tol(tol_), lb(lb_), ub(ub_) - { - } + raft::device_span bnd; + is_variable_free_t(f_t tol_, raft::device_span bnd_) : tol(tol_), bnd(bnd_) {} template __device__ bool operator()(tuple_t edge) { - auto var = thrust::get<2>(edge); - return abs(ub[var] - lb[var]) > tol; + auto var = thrust::get<2>(edge); + auto bounds = bnd[var]; + return abs(bounds.y - bounds.x) > tol; } }; -template +template struct assign_fixed_var_t { raft::device_span is_var_used; - raft::device_span variable_lower_bounds; - raft::device_span variable_upper_bounds; + raft::device_span variable_bounds; raft::device_span objective_coefficients; raft::device_span variable_mapping; raft::device_span fixed_assignment; assign_fixed_var_t(raft::device_span is_var_used_, - raft::device_span variable_lower_bounds_, - raft::device_span variable_upper_bounds_, + raft::device_span variable_bounds_, raft::device_span objective_coefficients_, raft::device_span variable_mapping_, raft::device_span fixed_assignment_) : is_var_used(is_var_used_), - variable_lower_bounds(variable_lower_bounds_), - variable_upper_bounds(variable_upper_bounds_), + variable_bounds(variable_bounds_), objective_coefficients(objective_coefficients_), variable_mapping(variable_mapping_), fixed_assignment(fixed_assignment_) @@ -73,40 +67,38 @@ struct assign_fixed_var_t { __device__ void operator()(i_t i) const { if (!is_var_used[i]) { - auto orig_v_idx = variable_mapping[i]; - fixed_assignment[orig_v_idx] = - (objective_coefficients[i] > 0) ? variable_lower_bounds[i] : variable_upper_bounds[i]; + auto orig_v_idx = variable_mapping[i]; + auto bounds = variable_bounds[i]; + fixed_assignment[orig_v_idx] = (objective_coefficients[i] > 0) ? bounds.x : bounds.y; } } }; -template +template struct elem_multi_t { raft::device_span coefficients; raft::device_span variables; raft::device_span obj_coefficients; - raft::device_span variable_lower_bounds; - raft::device_span variable_upper_bounds; + raft::device_span variable_bounds; elem_multi_t(raft::device_span coefficients_, raft::device_span variables_, raft::device_span obj_coefficients_, - raft::device_span variable_lower_bounds_, - raft::device_span variable_upper_bounds_) + raft::device_span variable_bounds_) : coefficients(coefficients_), variables(variables_), obj_coefficients(obj_coefficients_), - variable_lower_bounds(variable_lower_bounds_), - variable_upper_bounds(variable_upper_bounds_) + variable_bounds(variable_bounds_) { } __device__ f_t operator()(i_t i) const { - auto var = variables[i]; + auto var = variables[i]; + auto bounds = variable_bounds[i]; if (obj_coefficients[var] > 0) { - return variable_lower_bounds[var] * coefficients[i]; + return bounds.x * coefficients[i]; } else { - return variable_upper_bounds[var] * coefficients[i]; + return bounds.y * coefficients[i]; } } }; @@ -136,18 +128,16 @@ struct update_constraint_bounds_t { } }; -template +template struct unused_var_obj_offset_t { raft::device_span var_map; raft::device_span objective_coefficients; - raft::device_span lb; - raft::device_span ub; + raft::device_span bnd; unused_var_obj_offset_t(raft::device_span var_map_, raft::device_span objective_coefficients_, - raft::device_span lb_, - raft::device_span ub_) - : var_map(var_map_), objective_coefficients(objective_coefficients_), lb(lb_), ub(ub_) + raft::device_span bnd_) + : var_map(var_map_), objective_coefficients(objective_coefficients_), bnd(bnd_) { } @@ -156,7 +146,8 @@ struct unused_var_obj_offset_t { auto obj_coeff = objective_coefficients[i]; // in case both bounds are infinite if (obj_coeff == 0.) return 0.; - auto obj_off = (obj_coeff > 0) ? obj_coeff * lb[i] : obj_coeff * ub[i]; + auto bounds = bnd[i]; + auto obj_off = (obj_coeff > 0) ? obj_coeff * bounds.x : obj_coeff * bounds.y; return var_map[i] ? 0. : obj_off; } }; diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 43ea2b10f7..9afacd2bed 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -126,10 +126,10 @@ problem_t::problem_t( objective_coefficients(problem_.get_objective_coefficients(), problem_.get_handle_ptr()->get_stream()), variable_bounds(0, problem_.get_handle_ptr()->get_stream()), - variable_lower_bounds(problem_.get_variable_lower_bounds(), - problem_.get_handle_ptr()->get_stream()), - variable_upper_bounds(problem_.get_variable_upper_bounds(), - problem_.get_handle_ptr()->get_stream()), + // variable_lower_bounds(problem_.get_variable_lower_bounds(), + // problem_.get_handle_ptr()->get_stream()), + // variable_upper_bounds(problem_.get_variable_upper_bounds(), + // problem_.get_handle_ptr()->get_stream()), constraint_lower_bounds(problem_.get_constraint_lower_bounds(), problem_.get_handle_ptr()->get_stream()), constraint_upper_bounds(problem_.get_constraint_upper_bounds(), @@ -179,8 +179,8 @@ problem_t::problem_t(const problem_t& problem_) offsets(problem_.offsets, handle_ptr->get_stream()), objective_coefficients(problem_.objective_coefficients, handle_ptr->get_stream()), variable_bounds(problem_.variable_bounds, handle_ptr->get_stream()), - variable_lower_bounds(problem_.variable_lower_bounds, handle_ptr->get_stream()), - variable_upper_bounds(problem_.variable_upper_bounds, handle_ptr->get_stream()), + // variable_lower_bounds(problem_.variable_lower_bounds, handle_ptr->get_stream()), + // variable_upper_bounds(problem_.variable_upper_bounds, handle_ptr->get_stream()), constraint_lower_bounds(problem_.constraint_lower_bounds, handle_ptr->get_stream()), constraint_upper_bounds(problem_.constraint_upper_bounds, handle_ptr->get_stream()), combined_bounds(problem_.combined_bounds, handle_ptr->get_stream()), @@ -256,16 +256,16 @@ problem_t::problem_t(const problem_t& problem_, bool no_deep (!no_deep_copy) ? rmm::device_uvector(problem_.variable_bounds, handle_ptr->get_stream()) : rmm::device_uvector(problem_.variable_bounds.size(), handle_ptr->get_stream())), - variable_lower_bounds( - (!no_deep_copy) - ? rmm::device_uvector(problem_.variable_lower_bounds, handle_ptr->get_stream()) - : rmm::device_uvector(problem_.variable_lower_bounds.size(), - handle_ptr->get_stream())), - variable_upper_bounds( - (!no_deep_copy) - ? rmm::device_uvector(problem_.variable_upper_bounds, handle_ptr->get_stream()) - : rmm::device_uvector(problem_.variable_upper_bounds.size(), - handle_ptr->get_stream())), + // variable_lower_bounds( + // (!no_deep_copy) + // ? rmm::device_uvector(problem_.variable_lower_bounds, handle_ptr->get_stream()) + // : rmm::device_uvector(problem_.variable_lower_bounds.size(), + // handle_ptr->get_stream())), + // variable_upper_bounds( + // (!no_deep_copy) + // ? rmm::device_uvector(problem_.variable_upper_bounds, handle_ptr->get_stream()) + // : rmm::device_uvector(problem_.variable_upper_bounds.size(), + // handle_ptr->get_stream())), constraint_lower_bounds( (!no_deep_copy) ? rmm::device_uvector(problem_.constraint_lower_bounds, handle_ptr->get_stream()) @@ -398,8 +398,8 @@ void problem_t::check_problem_representation(bool check_transposed, // Check variable bounds are set and with the correct size if (!empty) { - cuopt_assert(!variable_lower_bounds.is_empty() && !variable_upper_bounds.is_empty(), - "Variable lower bounds and variable upper bounds must be set."); + // cuopt_assert(!variable_lower_bounds.is_empty() && !variable_upper_bounds.is_empty(), + // "Variable lower bounds and variable upper bounds must be set."); cuopt_assert(!variable_bounds.is_empty(), "Variable bounds must be set."); } cuopt_assert(variable_bounds.size() == 2 * objective_coefficients.size(), @@ -407,10 +407,10 @@ void problem_t::check_problem_representation(bool check_transposed, cuopt_assert(variable_bounds.size() == 2 * (std::size_t)n_variables, "Sizes for vectors related to the variables are not the same."); - cuopt_assert(variable_lower_bounds.size() == objective_coefficients.size(), - "Sizes for vectors related to the variables are not the same."); - cuopt_assert(variable_upper_bounds.size() == objective_coefficients.size(), - "Sizes for vectors related to the variables are not the same"); + // cuopt_assert(variable_lower_bounds.size() == objective_coefficients.size(), + // "Sizes for vectors related to the variables are not the same."); + // cuopt_assert(variable_upper_bounds.size() == objective_coefficients.size(), + // "Sizes for vectors related to the variables are not the same"); cuopt_assert(variable_upper_bounds.size() == (std::size_t)n_variables, "Sizes for vectors related to the variables are not the same."); cuopt_assert(variable_types.size() == (std::size_t)n_variables, @@ -931,10 +931,10 @@ typename problem_t::view_t problem_t::view() v.objective_coefficients = raft::device_span{objective_coefficients.data(), objective_coefficients.size()}; v.variable_bounds = make_span(variable_bounds); - v.variable_lower_bounds = - raft::device_span{variable_lower_bounds.data(), variable_lower_bounds.size()}; - v.variable_upper_bounds = - raft::device_span{variable_upper_bounds.data(), variable_upper_bounds.size()}; + // v.variable_lower_bounds = + // raft::device_span{variable_lower_bounds.data(), variable_lower_bounds.size()}; + // v.variable_upper_bounds = + // raft::device_span{variable_upper_bounds.data(), variable_upper_bounds.size()}; v.constraint_lower_bounds = raft::device_span{constraint_lower_bounds.data(), constraint_lower_bounds.size()}; v.constraint_upper_bounds = @@ -958,8 +958,8 @@ template void problem_t::resize_variables(size_t size) { variable_bounds.resize(size, handle_ptr->get_stream()); - variable_lower_bounds.resize(size, handle_ptr->get_stream()); - variable_upper_bounds.resize(size, handle_ptr->get_stream()); + // variable_lower_bounds.resize(size, handle_ptr->get_stream()); + // variable_upper_bounds.resize(size, handle_ptr->get_stream()); variable_types.resize(size, handle_ptr->get_stream()); objective_coefficients.resize(size, handle_ptr->get_stream()); is_binary_variable.resize(size, handle_ptr->get_stream()); @@ -995,14 +995,14 @@ void problem_t::insert_variables(variables_delta_t& h_vars) h_vars.variable_bounds.data(), h_vars.variable_bounds.size(), handle_ptr->get_stream()); - raft::copy(variable_lower_bounds.data() + n_variables, - h_vars.lower_bounds.data(), - h_vars.lower_bounds.size(), - handle_ptr->get_stream()); - raft::copy(variable_upper_bounds.data() + n_variables, - h_vars.upper_bounds.data(), - h_vars.upper_bounds.size(), - handle_ptr->get_stream()); + // raft::copy(variable_lower_bounds.data() + n_variables, + // h_vars.lower_bounds.data(), + // h_vars.lower_bounds.size(), + // handle_ptr->get_stream()); + // raft::copy(variable_upper_bounds.data() + n_variables, + // h_vars.upper_bounds.data(), + // h_vars.upper_bounds.size(), + // handle_ptr->get_stream()); raft::copy(variable_types.data() + n_variables, h_vars.variable_types.data(), h_vars.variable_types.size(), @@ -1244,18 +1244,18 @@ void problem_t::remove_given_variables(problem_t& original_p original_problem.variable_bounds.begin(), variable_bounds.begin()); variable_bounds.resize(variable_map.size(), handle_ptr->get_stream()); - thrust::gather(handle_ptr->get_thrust_policy(), - variable_map.begin(), - variable_map.end(), - original_problem.variable_lower_bounds.begin(), - variable_lower_bounds.begin()); - variable_lower_bounds.resize(variable_map.size(), handle_ptr->get_stream()); - thrust::gather(handle_ptr->get_thrust_policy(), - variable_map.begin(), - variable_map.end(), - original_problem.variable_upper_bounds.begin(), - variable_upper_bounds.begin()); - variable_upper_bounds.resize(variable_map.size(), handle_ptr->get_stream()); + // thrust::gather(handle_ptr->get_thrust_policy(), + // variable_map.begin(), + // variable_map.end(), + // original_problem.variable_lower_bounds.begin(), + // variable_lower_bounds.begin()); + // variable_lower_bounds.resize(variable_map.size(), handle_ptr->get_stream()); + // thrust::gather(handle_ptr->get_thrust_policy(), + // variable_map.begin(), + // variable_map.end(), + // original_problem.variable_upper_bounds.begin(), + // variable_upper_bounds.begin()); + // variable_upper_bounds.resize(variable_map.size(), handle_ptr->get_stream()); thrust::gather(handle_ptr->get_thrust_policy(), variable_map.begin(), variable_map.end(), diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index f0ec7e430c..9c16c8b0be 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -185,8 +185,8 @@ class problem_t { raft::device_span offsets; raft::device_span objective_coefficients; raft::device_span variable_bounds; - raft::device_span variable_lower_bounds; - raft::device_span variable_upper_bounds; + // raft::device_span variable_lower_bounds; + // raft::device_span variable_upper_bounds; raft::device_span constraint_lower_bounds; raft::device_span constraint_upper_bounds; raft::device_span variable_types; @@ -242,8 +242,8 @@ class problem_t { rmm::device_uvector objective_coefficients; using f_t2 = typename type_2::type; rmm::device_uvector variable_bounds; - rmm::device_uvector variable_lower_bounds; - rmm::device_uvector variable_upper_bounds; + // rmm::device_uvector variable_lower_bounds; + // rmm::device_uvector variable_upper_bounds; rmm::device_uvector constraint_lower_bounds; rmm::device_uvector constraint_upper_bounds; /* biggest between cstr lower and upper */ diff --git a/cpp/src/mip/problem/problem_helpers.cuh b/cpp/src/mip/problem/problem_helpers.cuh index ca9a982f12..ff1c8ce811 100644 --- a/cpp/src/mip/problem/problem_helpers.cuh +++ b/cpp/src/mip/problem/problem_helpers.cuh @@ -122,7 +122,7 @@ static void set_bounds_if_not_set(detail::problem_t& op_problem) transform_bounds_functor()); } -#if 1 +#if 0 // If variable bound was not set, set it to default value if (op_problem.variable_lower_bounds.is_empty() && !op_problem.objective_coefficients.is_empty()) { @@ -142,6 +142,8 @@ static void set_bounds_if_not_set(detail::problem_t& op_problem) op_problem.variable_upper_bounds.end(), std::numeric_limits::infinity()); } +#else + set_variable_bounds(op_problem); #endif if (op_problem.variable_types.is_empty() && !op_problem.objective_coefficients.is_empty()) { op_problem.variable_types.resize(op_problem.objective_coefficients.size(), @@ -293,11 +295,11 @@ static bool check_var_bounds_sanity(const detail::problem_t& problem) bool crossing_bounds_detected = thrust::any_of(problem.handle_ptr->get_thrust_policy(), thrust::counting_iterator(0), - thrust::counting_iterator((i_t)problem.variable_lower_bounds.size()), + thrust::counting_iterator((i_t)problem.variable_bounds.size()), [tolerance = problem.tolerances.presolve_absolute_tolerance, - lb = make_span(problem.variable_lower_bounds), - ub = make_span(problem.variable_upper_bounds)] __device__(i_t index) { - return (lb[index] > ub[index] + tolerance); + var_bnd = make_span(problem.variable_bounds)] __device__(i_t index) { + auto var_bounds = var_bnd[index]; + return (var_bounds.x > var_bounds.y + tolerance); }); return !crossing_bounds_detected; } @@ -324,12 +326,12 @@ static void round_bounds(detail::problem_t& problem) thrust::for_each(problem.handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(problem.n_variables), - [lb = make_span(problem.variable_lower_bounds), - ub = make_span(problem.variable_upper_bounds), - types = make_span(problem.variable_types)] __device__(i_t index) { + [bounds = make_span(problem.variable_bounds), + types = make_span(problem.variable_types)] __device__(i_t index) { if (types[index] == var_t::INTEGER) { - lb[index] = ceil(lb[index]); - ub[index] = floor(ub[index]); + using f_t2 = typename type_2::type; + auto bnd = bounds[index]; + bounds[index] = f_t2{ceil(bnd.x), floor(bnd.y)}; } }); } diff --git a/cpp/src/mip/problem/write_mps.cu b/cpp/src/mip/problem/write_mps.cu index cca3cd5b1e..ce3e562383 100644 --- a/cpp/src/mip/problem/write_mps.cu +++ b/cpp/src/mip/problem/write_mps.cu @@ -36,8 +36,7 @@ void problem_t::write_as_mps(const std::string& path) auto h_reverse_constraints = cuopt::host_copy(reverse_constraints, handle_ptr->get_stream()); auto h_reverse_offsets = cuopt::host_copy(reverse_offsets, handle_ptr->get_stream()); auto h_obj_coeffs = cuopt::host_copy(objective_coefficients, handle_ptr->get_stream()); - auto h_var_lb = cuopt::host_copy(variable_lower_bounds, handle_ptr->get_stream()); - auto h_var_ub = cuopt::host_copy(variable_upper_bounds, handle_ptr->get_stream()); + auto [h_var_lb, h_var_ub] = extract_host_bounds(variable_bounds, handle_ptr); auto h_cstr_lb = cuopt::host_copy(constraint_lower_bounds, handle_ptr->get_stream()); auto h_cstr_ub = cuopt::host_copy(constraint_upper_bounds, handle_ptr->get_stream()); auto h_var_types = cuopt::host_copy(variable_types, handle_ptr->get_stream()); diff --git a/cpp/tests/mip/CMakeLists.txt b/cpp/tests/mip/CMakeLists.txt index 020c537f6a..b9fd249a56 100644 --- a/cpp/tests/mip/CMakeLists.txt +++ b/cpp/tests/mip/CMakeLists.txt @@ -27,9 +27,6 @@ ConfigureTest(ELIM_VAR_REMAP_TEST ConfigureTest(STANDARDIZATION_TEST ${CMAKE_CURRENT_SOURCE_DIR}/bounds_standardization_test.cu ) -ConfigureTest(LB_TEST - ${CMAKE_CURRENT_SOURCE_DIR}/load_balancing_test.cu -) ConfigureTest(MULTI_PROBE_TEST ${CMAKE_CURRENT_SOURCE_DIR}/multi_probe_test.cu ) diff --git a/cpp/tests/mip/elim_var_remap_test.cu b/cpp/tests/mip/elim_var_remap_test.cu index aeb48fe5d6..edaae90810 100644 --- a/cpp/tests/mip/elim_var_remap_test.cu +++ b/cpp/tests/mip/elim_var_remap_test.cu @@ -100,8 +100,8 @@ void test_elim_var_remap(std::string test_instance) auto fixed_vars = select_k_random(problem.n_variables - 1, 5); for (auto& v : fixed_vars) { double v_val = -v - 1; - problem.variable_lower_bounds.set_element(v, v_val, handle_.get_stream()); - problem.variable_upper_bounds.set_element(v, v_val, handle_.get_stream()); + double2 val = double2{v_val, v_val}; + problem.variable_bounds.set_element(v, val, handle_.get_stream()); full_assignment.set_element(v, v_val, handle_.get_stream()); } // Set free var assignments to 0 @@ -182,8 +182,8 @@ void test_elim_var_solution(std::string test_instance) auto fixed_vars = select_k_random(standardized_problem.n_variables - 1, 5); for (auto& v : fixed_vars) { double v_val = opt_sol_1.get_solution().element(v, handle_.get_stream()); - sub_problem.variable_lower_bounds.set_element(v, v_val, handle_.get_stream()); - sub_problem.variable_upper_bounds.set_element(v, v_val, handle_.get_stream()); + double2 val = double2{v_val, v_val}; + sub_problem.variable_bounds.set_element(v, val, handle_.get_stream()); } handle_.sync_stream(); @@ -213,7 +213,9 @@ void test_elim_var_solution(std::string test_instance) TEST(mip_solve, elim_var_remap_test) { std::vector test_instances = { - "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; + //"mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; + "mip/50v-10-free-bound.mps", + "mip/neos5-free-bound.mps"}; for (const auto& test_instance : test_instances) { test_elim_var_remap(test_instance); } @@ -222,7 +224,9 @@ TEST(mip_solve, elim_var_remap_test) TEST(mip_solve, elim_var_remap_solution_test) { std::vector test_instances = { - "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; + //"mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; + "mip/50v-10-free-bound.mps", + "mip/neos5-free-bound.mps"}; for (const auto& test_instance : test_instances) { test_elim_var_solution(test_instance); } diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index 63cf93c792..23f11f2b59 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -58,9 +58,8 @@ std::tuple, std::vector, std::vector> select_k_ auto seed = std::random_device{}(); std::cerr << "Tested with seed " << seed << "\n"; problem.compute_n_integer_vars(); - auto v_lb = host_copy(problem.variable_lower_bounds); - auto v_ub = host_copy(problem.variable_upper_bounds); - auto int_var_id = host_copy(problem.integer_indices); + auto [v_lb, v_ub] = extract_host_bounds(problem.variable_bounds, problem.handle_ptr); + auto int_var_id = host_copy(problem.integer_indices); int_var_id.erase(std::remove_if(int_var_id.begin(), int_var_id.end(), [v_lb, v_ub](auto id) { @@ -209,7 +208,9 @@ void test_multi_probe(std::string path) TEST(presolve, multi_probe) { std::vector test_instances = { - "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; + //"mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; + "mip/50v-10-free-bound.mps", + "mip/neos5-free-bound.mps"}; for (const auto& test_instance : test_instances) { std::cout << "Running: " << test_instance << std::endl; auto path = make_path_absolute(test_instance); From 4987b67d96ce5726f323682c121db9cd52596f4a Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Wed, 3 Sep 2025 19:32:08 -0400 Subject: [PATCH 04/10] debug --- cpp/src/mip/diversity/assignment_hash_map.cu | 25 ++++ cpp/src/mip/diversity/diversity_manager.cu | 121 ++++++++++++++++ .../mip/feasibility_jump/feasibility_jump.cu | 72 +++++++++- cpp/src/mip/local_search/local_search.cu | 5 + .../local_search/rounding/constraint_prop.cu | 83 ++++++----- cpp/src/mip/presolve/bounds_presolve.cu | 4 +- .../mip/presolve/trivial_presolve_helpers.cuh | 2 +- cpp/src/mip/problem/problem.cu | 6 +- cpp/src/mip/solution/solution.cu | 35 ++++- cpp/src/mip/solver.cu | 34 ++++- cpp/src/mip/utils.cuh | 16 +++ cpp/src/utilities/copy_helpers.hpp | 1 + cpp/tests/mip/elim_var_remap_test.cu | 8 +- cpp/tests/mip/multi_probe_test.cu | 17 ++- cpp/tests/mip/termination_test.cu | 134 +++++++++--------- cpp/tests/mip/unit_test.cu | 125 ++++++++-------- 16 files changed, 500 insertions(+), 188 deletions(-) diff --git a/cpp/src/mip/diversity/assignment_hash_map.cu b/cpp/src/mip/diversity/assignment_hash_map.cu index 91ef05bd1f..803969fa86 100644 --- a/cpp/src/mip/diversity/assignment_hash_map.cu +++ b/cpp/src/mip/diversity/assignment_hash_map.cu @@ -80,6 +80,8 @@ template void assignment_hash_map_t::fill_integer_assignment(solution_t& solution) { static_assert(sizeof(f_t) == sizeof(size_t), "f_t must be double precision"); + std::cerr << "integer_indices size " << solution.problem_ptr->integer_indices.size() + << " integer_assignment " << integer_assignment.size() << "\n"; thrust::gather(solution.handle_ptr->get_thrust_policy(), solution.problem_ptr->integer_indices.begin(), solution.problem_ptr->integer_indices.end(), @@ -91,12 +93,27 @@ template size_t assignment_hash_map_t::hash_solution(solution_t& solution) { const int TPB = 1024; + + std::cerr << "has_solution pt 0\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + fill_integer_assignment(solution); + std::cerr << "has_solution pt 1\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + thrust::fill( solution.handle_ptr->get_thrust_policy(), reduction_buffer.begin(), reduction_buffer.end(), 0); hash_solution_kernel <<<(integer_assignment.size() + TPB - 1) / TPB, TPB, 0, solution.handle_ptr->get_stream()>>>( cuopt::make_span(integer_assignment), cuopt::make_span(reduction_buffer)); + std::cerr << "has_solution pt 2\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); // Get the number of blocks used in the hash_solution_kernel int num_blocks = (integer_assignment.size() + TPB - 1) / TPB; @@ -119,6 +136,10 @@ size_t assignment_hash_map_t::hash_solution(solution_t& solu temp_storage.resize(temp_storage_bytes, solution.handle_ptr->get_stream()); d_temp_storage = temp_storage.data(); + std::cerr << "has_solution pt 3\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); // Run reduction cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes, @@ -128,6 +149,10 @@ size_t assignment_hash_map_t::hash_solution(solution_t& solu combine_hash(), 0, solution.handle_ptr->get_stream()); + std::cerr << "has_solution pt 4\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); // Return early since we've already computed the hash sum return hash_sum.value(solution.handle_ptr->get_stream()); diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index aa4aa1857c..df98274c82 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -114,10 +114,34 @@ bool diversity_manager_t::run_local_search(solution_t& solut { // i_t ls_mab_option = mab_ls.select_mab_option(); // mab_ls_config_t::get_local_search_and_lm_from_config(ls_mab_option, ls_config); + int dev; + cudaGetDevice(&dev); + std::cerr << "run_local_search pt -1\n device " << dev << "\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); assignment_hash_map.insert(solution); constexpr i_t skip_solutions_threshold = 3; + std::cerr << "run_local_search pt 0\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); if (assignment_hash_map.check_skip_solution(solution, skip_solutions_threshold)) { return false; } + std::cerr << "run_local_search pt 1\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); ls.run_local_search(solution, weights, timer, ls_config); + std::cerr << "run_local_search pt 2\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); return true; } @@ -306,11 +330,19 @@ bool diversity_manager_t::run_presolve(f_t time_limit) stats.presolve_time = timer.elapsed_time(); return false; } + problem_ptr->handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(problem_ptr->handle_ptr->get_stream()); + cudaDeviceSynchronize(); + std::cerr << "pt 0\n"; if (termination_criterion_t::NO_UPDATE != term_crit) { ls.constraint_prop.bounds_update.set_updated_bounds(*problem_ptr); trivial_presolve(*problem_ptr); if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; } } + problem_ptr->handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(problem_ptr->handle_ptr->get_stream()); + cudaDeviceSynchronize(); + std::cerr << "pt 1\n"; if (!problem_ptr->empty) { // do the resizing no-matter what, bounds presolve might not change the bounds but initial // trivial presolve might have @@ -319,31 +351,66 @@ bool diversity_manager_t::run_presolve(f_t time_limit) *problem_ptr, ls.constraint_prop.bounds_update); if (!check_bounds_sanity(*problem_ptr)) { return false; } } + problem_ptr->handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(problem_ptr->handle_ptr->get_stream()); + cudaDeviceSynchronize(); + std::cerr << "pt 2\n"; stats.presolve_time = presolve_timer.elapsed_time(); lp_optimal_solution.resize(problem_ptr->n_variables, problem_ptr->handle_ptr->get_stream()); problem_ptr->handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(problem_ptr->handle_ptr->get_stream()); cudaDeviceSynchronize(); + std::cerr << "pt 3\n"; return true; } template void diversity_manager_t::generate_quick_feasible_solution() { + std::cerr << "generate_fast_solution_time pt 0\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + solution_t solution(*problem_ptr); + std::cerr << "generate_fast_solution_time pt 1\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); // min 1 second, max 10 seconds const f_t generate_fast_solution_time = std::min(diversity_config_t::max_fast_sol_time, std::max(1., timer.remaining_time() / 20.)); timer_t sol_timer(generate_fast_solution_time); // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); + std::cerr << "generate_fast_solution_time pt 2\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); if (solution.get_feasible()) { population.run_solution_callbacks(solution); initial_sol_vector.emplace_back(std::move(solution)); problem_ptr->handle_ptr->sync_stream(); solution_t searched_sol(initial_sol_vector.back()); + std::cerr << "generate_fast_solution_time pt 2a\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); ls_config_t ls_config; + std::cerr << "generate_fast_solution_time pt 3\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); run_local_search(searched_sol, population.weights, sol_timer, ls_config); + std::cerr << "generate_fast_solution_time pt 4\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); population.run_solution_callbacks(searched_sol); + std::cerr << "generate_fast_solution_time pt 5\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); initial_sol_vector.emplace_back(std::move(searched_sol)); auto& feas_sol = initial_sol_vector.back().get_feasible() ? initial_sol_vector.back() @@ -395,11 +462,21 @@ solution_t diversity_manager_t::run_solver() cuopt::scope_guard([&]() { stats.total_solve_time = timer.elapsed_time(); }); // after every change to the problem, we should resize all the relevant vars // we need to encapsulate that to prevent repetitions + std::cerr << "run_solver pt 0\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + ls.resize_vectors(*problem_ptr, problem_ptr->handle_ptr); ls.constraint_prop.bounds_update.resize(*problem_ptr); problem_ptr->check_problem_representation(true); // have the structure ready for reusing later problem_ptr->compute_integer_fixed_problem(); + std::cerr << "run_solver pt 1\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + // test problem is not ii cuopt_func_call( ls.constraint_prop.bounds_update.calculate_activity_on_problem_bounds(*problem_ptr)); @@ -407,9 +484,21 @@ solution_t diversity_manager_t::run_solver() ls.constraint_prop.bounds_update.calculate_infeasible_redundant_constraints(*problem_ptr), "The problem must not be ii"); population.initialize_population(); + std::cerr << "run_solver pt 2\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); if (check_b_b_preemption()) { return population.best_feasible(); } + std::cerr << "run_solver pt 2a\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); // before probing cache or LP, run FJ to generate initial primal feasible solution if (!from_dir) { generate_quick_feasible_solution(); } + std::cerr << "run_solver pt 2b\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); constexpr f_t time_ratio_of_probing_cache = diversity_config_t::time_ratio_of_probing_cache; constexpr f_t max_time_on_probing = diversity_config_t::max_time_on_probing; f_t time_for_probing_cache = @@ -417,8 +506,20 @@ solution_t diversity_manager_t::run_solver() timer_t probing_timer{time_for_probing_cache}; if (check_b_b_preemption()) { return population.best_feasible(); } if (!fj_only_run) { + std::cerr << "run_solver pt 2c\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); + std::cerr << "run_solver pt 2d\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); } + std::cerr << "run_solver pt 3\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); if (check_b_b_preemption()) { return population.best_feasible(); } lp_state_t& lp_state = problem_ptr->lp_state; @@ -470,10 +571,20 @@ solution_t diversity_manager_t::run_solver() // in case the pdlp returned var boudns that are out of bounds clamp_within_var_bounds(lp_optimal_solution, problem_ptr, problem_ptr->handle_ptr); } + std::cerr << "run_solver pt 4\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + population.allocate_solutions(); if (check_b_b_preemption()) { return population.best_feasible(); } // generate a population with 5 solutions(FP+FJ) generate_initial_solutions(); + std::cerr << "run_solver pt 5\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + if (context.settings.benchmark_info_ptr != nullptr) { context.settings.benchmark_info_ptr->objective_of_initial_population = population.best_feasible().get_user_objective(); @@ -485,7 +596,17 @@ solution_t diversity_manager_t::run_solver() } if (timer.check_time_limit()) { return population.best_feasible(); } + std::cerr << "run_solver pt 6\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + main_loop(); + std::cerr << "run_solver pt 7\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + return population.best_feasible(); }; diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 15bf41bed3..ad06ca92d2 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -827,6 +827,10 @@ void fj_t::refresh_lhs_and_violation(const rmm::cuda_stream_view& stre template i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) { + std::cerr << "host_loop pt 0\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); auto& data = *climbers[climber_idx]; auto v = data.view(); // == climber_views[climber_idx] @@ -835,6 +839,10 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; solution.compute_feasibility(); + std::cerr << "host_loop pt 1\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); if (settings.feasibility_run) { objective_weight.set_value_to_zero_async(handle_ptr->get_stream()); } @@ -869,13 +877,25 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) objective_weight.value(climber_stream)); } - if (!limit_reached) { run_step_device(climber_stream, climber_idx); } + std::cerr << "host_loop pt 2\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + if (!limit_reached) { run_step_device(climber_stream, climber_idx, false); } + std::cerr << "host_loop pt 3\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); // periodically recompute the LHS and violation scores // to correct any accumulated numerical errors if (steps % settings.parameters.lhs_refresh_period == 0) { refresh_lhs_and_violation(climber_stream, climber_idx); } + std::cerr << "host_loop pt 4\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); // periodically synchronize and check the latest solution // feasible solution found!*view.break_condition @@ -888,6 +908,10 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) i_t iterations = data.iterations.value(climber_stream); // make sure we have the current incumbent saved (e.g. in the case of a timeout) update_best_solution_kernel<<<1, blocks_resetmoves, 0, climber_stream>>>(v); + std::cerr << "host_loop pt 5\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); // check feasibility with the relative tolerance rather than the violation score raft::copy(solution.assignment.data(), data.best_assignment.data(), @@ -897,7 +921,15 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) // this solution cost computation with the changing(or not changing) weights is needed to // decide whether we reset the best objective on the FIRST_FEASIBLE mode. once we get rid of // FIRST_FEASIBLE mode, we can remove the following too. + std::cerr << "host_loop pt 6\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); bool is_feasible = solution.compute_feasibility(); + std::cerr << "host_loop pt 7\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); solution.handle_ptr->sync_stream(); if (limit_reached) { break; } @@ -1035,6 +1067,11 @@ i_t fj_t::solve(solution_t& solution) pb_ptr->check_problem_representation(true); resize_vectors(solution.handle_ptr); + std::cerr << "fj solve pt 0\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + bool is_initial_feasible = solution.compute_feasibility(); // if we're in rounding mode, split the time/iteration limit between the first and second stage cuopt_assert(settings.parameters.rounding_second_stage_split >= 0 && @@ -1064,14 +1101,29 @@ i_t fj_t::solve(solution_t& solution) handle_ptr->get_stream()); } + std::cerr << "fj solve pt 1\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + climber_init(0); RAFT_CHECK_CUDA(handle_ptr->get_stream()); handle_ptr->sync_stream(); + std::cerr << "fj solve pt 2\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + i_t iterations = host_loop(solution); RAFT_CHECK_CUDA(handle_ptr->get_stream()); handle_ptr->sync_stream(); + std::cerr << "fj solve pt 3\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + f_t effort_rate = (f_t)iterations / timer.elapsed_time(); // If we're in rounding mode and some fractionals remain: round them all @@ -1083,11 +1135,24 @@ i_t fj_t::solve(solution_t& solution) settings.iteration_limit * settings.parameters.rounding_second_stage_split; round_remaining_fractionals(solution); + std::cerr << "fj solve pt 4\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + // if time limit exceeded: round all remaining fractionals if any by nearest rounding. if (climbers[0]->fractional_variables.set_size.value(handle_ptr->get_stream()) > 0) { solution.round_nearest(); + std::cerr << "fj solve pt 5\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); } } + std::cerr << "fj solve pt 6\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); CUOPT_LOG_TRACE("GPU solver took %g", timer.elapsed_time()); CUOPT_LOG_TRACE("limit reached, effort rate %g steps/secm %d steps", effort_rate, iterations); @@ -1106,6 +1171,11 @@ i_t fj_t::solve(solution_t& solution) cuopt_assert(solution.test_number_all_integer(), "All integers must be rounded"); bool is_new_feasible = solution.compute_feasibility(); + std::cerr << "fj solve pt 7\n"; + solution.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + if (is_initial_feasible && !is_new_feasible) { CUOPT_LOG_ERROR( "Feasibility jump caused feasible solution to become infeasible\n" diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index a2eda273b3..8608a6c05a 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -67,12 +67,15 @@ void local_search_t::generate_fast_solution(solution_t& solu while (!timer.check_time_limit()) { timer_t constr_prop_timer = timer_t(std::min(timer.remaining_time(), 2.)); // do constraint prop on lp optimal solution + std::cerr << "apply round\n"; 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()); // run fj on the solution + std::cerr << "solve\n"; fj.solve(solution); + std::cerr << "solve done\n"; // TODO check if FJ returns the same solution // check if the solution is feasible if (solution.compute_feasibility()) { return; } @@ -112,8 +115,10 @@ bool local_search_t::run_local_search(solution_t& solution, rd = ls_method_t::FJ_ANNEALING; } if (rd == ls_method_t::FJ_LINE_SEGMENT && lp_optimal_exists) { + std::cerr << "run_local_search pt 0\n"; is_feas = run_fj_line_segment(solution, timer, ls_config); } else { + std::cerr << "run_local_search pt 1\n"; is_feas = run_fj_annealing(solution, timer, ls_config); } return is_feas; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index a413af0e38..745feccca1 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -475,7 +475,7 @@ struct is_bound_fixed_t { auto v_bnd = bnd[idx]; auto v_lb = v_bnd.x; auto v_ub = v_bnd.y; - auto ov_bnd = bnd[idx]; + auto ov_bnd = original_bnd[idx]; auto o_lb = ov_bnd.x; auto o_ub = ov_bnd.y; bool is_singleton = @@ -725,12 +725,12 @@ constraint_prop_t::generate_bulk_rounding_vector( cuda::std::tie(first_probe, second_probe) = generate_double_probing_pair(sol, orig_sol, unset_var_idx, probing_config, false); } - cuopt_assert(test_var_out_of_bounds( - orig_sol, unset_var_idx, first_probe, int_tol, sol.handle_ptr->get_stream()), - "Variable out of original bounds!"); - cuopt_assert(test_var_out_of_bounds( - orig_sol, unset_var_idx, second_probe, int_tol, sol.handle_ptr->get_stream()), - "Variable out of original bounds!"); + cuopt_assert( + test_var_out_of_bounds(orig_sol, unset_var_idx, first_probe, int_tol, sol.handle_ptr), + "Variable out of original bounds!"); + cuopt_assert( + test_var_out_of_bounds(orig_sol, unset_var_idx, second_probe, int_tol, sol.handle_ptr), + "Variable out of original bounds!"); // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( // unset_var_idx, sol.handle_ptr->get_stream()) <= first_probe + int_tol && // first_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( @@ -758,12 +758,12 @@ constraint_prop_t::generate_bulk_rounding_vector( int_tol); if (val_to_round == second_probe) { second_probe = first_probe; } } - cuopt_assert(test_var_out_of_bounds( - orig_sol, unset_var_idx, val_to_round, int_tol, sol.handle_ptr->get_stream()), - "Variable out of original bounds!"); - cuopt_assert(test_var_out_of_bounds( - orig_sol, unset_var_idx, second_probe, int_tol, sol.handle_ptr->get_stream()), - "Variable out of original bounds!"); + cuopt_assert( + test_var_out_of_bounds(orig_sol, unset_var_idx, val_to_round, int_tol, sol.handle_ptr), + "Variable out of original bounds!"); + cuopt_assert( + test_var_out_of_bounds(orig_sol, unset_var_idx, second_probe, int_tol, sol.handle_ptr), + "Variable out of original bounds!"); // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( // unset_var_idx, sol.handle_ptr->get_stream()) <= val_to_round + int_tol && // val_to_round - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( @@ -803,28 +803,8 @@ struct extract_bounds_t { template void constraint_prop_t::set_host_bounds(const solution_t& sol) { - // cuopt_assert(sol.problem_ptr->variable_lower_bounds.size() == multi_probe.host_lb.size(), - // "size of variable lower bound mismatch"); - // raft::copy(multi_probe.host_lb.data(), - // sol.problem_ptr->variable_lower_bounds.data(), - // sol.problem_ptr->variable_lower_bounds.size(), - // sol.handle_ptr->get_stream()); - // cuopt_assert(sol.problem_ptr->variable_upper_bounds.size() == multi_probe.host_ub.size(), - // "size of variable upper bound mismatch"); - // raft::copy(multi_probe.host_ub.data(), - // sol.problem_ptr->variable_upper_bounds.data(), - // sol.problem_ptr->variable_upper_bounds.size(), - // sol.handle_ptr->get_stream()); - cuopt_assert(sol.problem_ptr->variable_bounds.size() == multi_probe.host_lb.size(), - "size of variable lower bound mismatch"); - cuopt_assert(sol.problem_ptr->variable_bounds.size() == multi_probe.host_ub.size(), - "size of variable upper bound mismatch"); - thrust::transform(sol.handle_ptr->get_thrust_policy(), - sol.problem_ptr->variable_bounds.begin(), - sol.problem_ptr->variable_bounds.end(), - thrust::make_zip_iterator( - thrust::make_tuple(multi_probe.host_lb.begin(), multi_probe.host_ub.begin())), - [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); + std::tie(multi_probe.host_lb, multi_probe.host_ub) = + extract_host_bounds(sol.problem_ptr->variable_bounds, sol.handle_ptr); } template @@ -939,6 +919,9 @@ bool constraint_prop_t::find_integer( multi_probe.settings.iteration_limit = 50; multi_probe.settings.time_limit = max_timer.remaining_time(); multi_probe.resize(*sol.problem_ptr); + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer pt 0\n"; if (max_timer.check_time_limit()) { CUOPT_LOG_DEBUG("Time limit is reached before bounds prop rounding!"); sol.round_nearest(); @@ -946,6 +929,9 @@ bool constraint_prop_t::find_integer( cuopt_func_call(orig_sol.test_variable_bounds()); return orig_sol.compute_feasibility(); } + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer pt 1\n"; raft::copy(unset_integer_vars.data(), sol.problem_ptr->integer_indices.data(), sol.problem_ptr->n_integer_vars, @@ -957,12 +943,24 @@ bool constraint_prop_t::find_integer( cuopt_func_call(orig_sol.test_variable_bounds()); return orig_sol.compute_feasibility(); } + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer pt 2\n"; // this is needed for the sort inside of the loop bool problem_ii = is_problem_ii(*sol.problem_ptr); + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer pt 2a\n"; // if the problem is ii, run the bounds prop in the beginning if (problem_ii) { + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer pt 3\n"; bool bounds_repaired = bounds_repair.repair_problem(*sol.problem_ptr, *orig_sol.problem_ptr, timer, sol.handle_ptr); + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer pt 4\n"; if (bounds_repaired) { CUOPT_LOG_DEBUG("Initial ii is repaired by bounds repair!"); } else { @@ -972,14 +970,23 @@ bool constraint_prop_t::find_integer( } rounding_ii = true; } + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer pt 5\n"; } // do the sort if the problem is not ii. crossing bounds might cause some issues on the sort order else { // this is a sort to have initial shuffling, so that stable sort within will keep the order and // some randomness will be achieved sort_by_interval_and_frac(sol, make_span(unset_integer_vars), rng); + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer pt 6\n"; } set_host_bounds(sol); + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer pt 7\n"; size_t set_count = 0; bool timeout_happened = false; i_t n_failed_repair_iterations = 0; @@ -1129,7 +1136,13 @@ bool constraint_prop_t::apply_round( temp_sol.problem_ptr = &p; f_t bounds_prop_start_time = max_timer.remaining_time(); cuopt_func_call(temp_sol.test_variable_bounds(false)); + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer\n"; bool sol_found = find_integer(temp_sol, sol, lp_run_time_after_feasible, timer, probing_config); + sol.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); + std::cerr << "find_integer done\n"; f_t bounds_prop_end_time = max_timer.remaining_time(); repair_stats.total_time_spent_on_bounds_prop += bounds_prop_start_time - bounds_prop_end_time; diff --git a/cpp/src/mip/presolve/bounds_presolve.cu b/cpp/src/mip/presolve/bounds_presolve.cu index 7ba86dd32d..800abd2d12 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cu +++ b/cpp/src/mip/presolve/bounds_presolve.cu @@ -323,8 +323,8 @@ template void bound_presolve_t::set_updated_bounds( const raft::handle_t* handle_ptr, raft::device_span::type> output_bounds) { - cuopt_assert(ub.size() == output_bounds.size(), "size of variable upper bound mismatch"); - cuopt_assert(lb.size() == output_bounds.size(), "size of variable lower bound mismatch"); + cuopt_assert(upd.ub.size() == output_bounds.size(), "size of variable upper bound mismatch"); + cuopt_assert(upd.lb.size() == output_bounds.size(), "size of variable lower bound mismatch"); thrust::transform(handle_ptr->get_thrust_policy(), thrust::make_zip_iterator(thrust::make_tuple(upd.lb.begin(), upd.ub.begin())), thrust::make_zip_iterator(thrust::make_tuple(upd.lb.end(), upd.ub.end())), diff --git a/cpp/src/mip/presolve/trivial_presolve_helpers.cuh b/cpp/src/mip/presolve/trivial_presolve_helpers.cuh index 22736a3113..ef5ce1af0e 100644 --- a/cpp/src/mip/presolve/trivial_presolve_helpers.cuh +++ b/cpp/src/mip/presolve/trivial_presolve_helpers.cuh @@ -94,7 +94,7 @@ struct elem_multi_t { __device__ f_t operator()(i_t i) const { auto var = variables[i]; - auto bounds = variable_bounds[i]; + auto bounds = variable_bounds[var]; if (obj_coefficients[var] > 0) { return bounds.x * coefficients[i]; } else { diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 9afacd2bed..3728c9d40b 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -402,17 +402,15 @@ void problem_t::check_problem_representation(bool check_transposed, // "Variable lower bounds and variable upper bounds must be set."); cuopt_assert(!variable_bounds.is_empty(), "Variable bounds must be set."); } - cuopt_assert(variable_bounds.size() == 2 * objective_coefficients.size(), + cuopt_assert(variable_bounds.size() == objective_coefficients.size(), "Sizes for vectors related to the variables are not the same."); - cuopt_assert(variable_bounds.size() == 2 * (std::size_t)n_variables, + cuopt_assert(variable_bounds.size() == (std::size_t)n_variables, "Sizes for vectors related to the variables are not the same."); // cuopt_assert(variable_lower_bounds.size() == objective_coefficients.size(), // "Sizes for vectors related to the variables are not the same."); // cuopt_assert(variable_upper_bounds.size() == objective_coefficients.size(), // "Sizes for vectors related to the variables are not the same"); - cuopt_assert(variable_upper_bounds.size() == (std::size_t)n_variables, - "Sizes for vectors related to the variables are not the same."); cuopt_assert(variable_types.size() == (std::size_t)n_variables, "Sizes for vectors related to the variables are not the same."); // Check constraints bounds sizes diff --git a/cpp/src/mip/solution/solution.cu b/cpp/src/mip/solution/solution.cu index 82fdadcbef..929ba2b810 100644 --- a/cpp/src/mip/solution/solution.cu +++ b/cpp/src/mip/solution/solution.cu @@ -319,10 +319,31 @@ template bool solution_t::compute_feasibility() { n_feasible_constraints.set_value_to_zero_async(handle_ptr->get_stream()); + std::cerr << "compute_feasibility pt 0\n"; + handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(handle_ptr->get_stream()); + cudaDeviceSynchronize(); compute_constraints(); + std::cerr << "compute_feasibility pt 1\n"; + handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(handle_ptr->get_stream()); + cudaDeviceSynchronize(); + std::cerr << "compute_feasibility pt 1a\n"; compute_objective(); + std::cerr << "compute_feasibility pt 2\n"; + handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(handle_ptr->get_stream()); + cudaDeviceSynchronize(); compute_infeasibility(); + std::cerr << "compute_feasibility pt 3\n"; + handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(handle_ptr->get_stream()); + cudaDeviceSynchronize(); compute_number_of_integers(); + std::cerr << "compute_feasibility pt 4\n"; + handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(handle_ptr->get_stream()); + cudaDeviceSynchronize(); i_t h_n_feas_constraints = n_feasible_constraints.value(handle_ptr->get_stream()); is_feasible = h_n_feas_constraints == problem_ptr->n_constraints && test_number_all_integer(); CUOPT_LOG_TRACE("is_feasible %d n_feasible_cstr %d all_cstr %d", @@ -335,11 +356,23 @@ bool solution_t::compute_feasibility() template void solution_t::compute_objective() { + std::cerr << "compute_objective pt 0\n"; + handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(handle_ptr->get_stream()); + cudaDeviceSynchronize(); h_obj = compute_objective_from_vec( - assignment, problem_ptr->objective_coefficients, handle_ptr->get_stream()); + assignment, problem_ptr->objective_coefficients, handle_ptr); + std::cerr << "compute_objective pt 1\n"; + handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(handle_ptr->get_stream()); + cudaDeviceSynchronize(); // to save from memory transactions, don't update the device objective // when needed we can update the device objective here h_user_obj = problem_ptr->get_user_obj_from_solver_obj(h_obj); + std::cerr << "compute_objective pt 2\n"; + handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(handle_ptr->get_stream()); + cudaDeviceSynchronize(); } template diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index 0f2117991f..f7a83e5ddc 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -148,6 +148,11 @@ solution_t mip_solver_t::run_solver() return sol; } + std::cerr << "pt 4\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + namespace dual_simplex = cuopt::linear_programming::dual_simplex; std::future branch_and_bound_status_future; dual_simplex::user_problem_t branch_and_bound_problem; @@ -156,9 +161,19 @@ solution_t mip_solver_t::run_solver() branch_and_bound_solution_helper_t solution_helper(&dm, branch_and_bound_settings); dual_simplex::mip_solution_t branch_and_bound_solution(1); + std::cerr << "pt 5\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + if (!context.settings.heuristics_only) { // Convert the presolved problem to dual_simplex::user_problem_t op_problem_.get_host_user_problem(branch_and_bound_problem); + std::cerr << "pt 6\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + // Resize the solution now that we know the number of columns/variables branch_and_bound_solution.resize(branch_and_bound_problem.num_cols); @@ -207,9 +222,18 @@ solution_t mip_solver_t::run_solver() branch_and_bound.get(), std::ref(branch_and_bound_solution)); } + std::cerr << "pt 7\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); + std::cerr << "pt 7a\n"; // Start the primal heuristics auto sol = dm.run_solver(); + std::cerr << "pt 8\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); if (!context.settings.heuristics_only) { // Wait for the branch and bound to finish auto bb_status = branch_and_bound_status_future.get(); @@ -221,9 +245,17 @@ solution_t mip_solver_t::run_solver() context.stats.num_nodes = branch_and_bound_solution.nodes_explored; context.stats.num_simplex_iterations = branch_and_bound_solution.simplex_iterations; } + std::cerr << "pt 9\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); sol.compute_feasibility(); - rmm::device_scalar is_feasible(sol.handle_ptr->get_stream()); + rmm::device_scalar is_feasible(context.handle_ptr->get_stream()); sol.test_variable_bounds(true, is_feasible.data()); + std::cerr << "pt 10\n"; + context.handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); + cudaDeviceSynchronize(); // test_variable_bounds clears is_feasible if the test is failed if (!is_feasible.value(sol.handle_ptr->get_stream())) { CUOPT_LOG_ERROR( diff --git a/cpp/src/mip/utils.cuh b/cpp/src/mip/utils.cuh index 43872861f3..c24e5928eb 100644 --- a/cpp/src/mip/utils.cuh +++ b/cpp/src/mip/utils.cuh @@ -218,6 +218,22 @@ bool check_integer_equal_on_indices(const rmm::device_uvector& indices, }); } +template +f_t compute_objective_from_vec(const rmm::device_uvector& assignment, + const rmm::device_uvector& objective_coefficients, + const raft::handle_t* handle_ptr) +{ + cuopt_assert(assignment.size() == objective_coefficients.size(), "Size mismatch!"); + std::cerr << "assignment " << assignment.size() << "\n"; + std::cerr << "objective_coefficients " << objective_coefficients.size() << "\n"; + f_t computed_obj = thrust::inner_product(handle_ptr->get_thrust_policy(), + assignment.begin(), + assignment.end(), + objective_coefficients.begin(), + 0.); + return computed_obj; +} + template f_t compute_objective_from_vec(const rmm::device_uvector& assignment, const rmm::device_uvector& objective_coefficients, diff --git a/cpp/src/utilities/copy_helpers.hpp b/cpp/src/utilities/copy_helpers.hpp index d491e29ae1..d9c3278000 100644 --- a/cpp/src/utilities/copy_helpers.hpp +++ b/cpp/src/utilities/copy_helpers.hpp @@ -308,6 +308,7 @@ std::tuple, std::vector> extract_host_bounds( variable_bounds.end(), thrust::make_zip_iterator(thrust::make_tuple(var_lb.begin(), var_ub.begin())), [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); + handle_ptr->sync_stream(); auto h_var_lb = cuopt::host_copy(var_lb); auto h_var_ub = cuopt::host_copy(var_ub); return std::make_tuple(h_var_lb, h_var_ub); diff --git a/cpp/tests/mip/elim_var_remap_test.cu b/cpp/tests/mip/elim_var_remap_test.cu index edaae90810..c486d98c81 100644 --- a/cpp/tests/mip/elim_var_remap_test.cu +++ b/cpp/tests/mip/elim_var_remap_test.cu @@ -213,9 +213,7 @@ void test_elim_var_solution(std::string test_instance) TEST(mip_solve, elim_var_remap_test) { std::vector test_instances = { - //"mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; - "mip/50v-10-free-bound.mps", - "mip/neos5-free-bound.mps"}; + "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; for (const auto& test_instance : test_instances) { test_elim_var_remap(test_instance); } @@ -224,9 +222,7 @@ TEST(mip_solve, elim_var_remap_test) TEST(mip_solve, elim_var_remap_solution_test) { std::vector test_instances = { - //"mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; - "mip/50v-10-free-bound.mps", - "mip/neos5-free-bound.mps"}; + "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; for (const auto& test_instance : test_instances) { test_elim_var_solution(test_instance); } diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index 23f11f2b59..1473c84bff 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -60,12 +60,13 @@ std::tuple, std::vector, std::vector> select_k_ problem.compute_n_integer_vars(); auto [v_lb, v_ub] = extract_host_bounds(problem.variable_bounds, problem.handle_ptr); auto int_var_id = host_copy(problem.integer_indices); - int_var_id.erase(std::remove_if(int_var_id.begin(), - int_var_id.end(), - [v_lb, v_ub](auto id) { - return !(std::isfinite(v_lb[id]) && std::isfinite(v_ub[id])); - }), - int_var_id.end()); + int_var_id.erase( + std::remove_if(int_var_id.begin(), + int_var_id.end(), + [v_lb_sp = v_lb, v_ub_sp = v_ub](auto id) { + return !(std::isfinite(v_lb_sp[id]) && std::isfinite(v_ub_sp[id])); + }), + int_var_id.end()); sample_size = std::min(sample_size, static_cast(int_var_id.size())); std::vector random_int_vars; std::mt19937 m{seed}; @@ -208,9 +209,7 @@ void test_multi_probe(std::string path) TEST(presolve, multi_probe) { std::vector test_instances = { - //"mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; - "mip/50v-10-free-bound.mps", - "mip/neos5-free-bound.mps"}; + "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; for (const auto& test_instance : test_instances) { std::cout << "Running: " << test_instance << std::endl; auto path = make_path_absolute(test_instance); diff --git a/cpp/tests/mip/termination_test.cu b/cpp/tests/mip/termination_test.cu index 54be62c591..a33a1d75e4 100644 --- a/cpp/tests/mip/termination_test.cu +++ b/cpp/tests/mip/termination_test.cu @@ -48,36 +48,36 @@ namespace cuopt::linear_programming::test { constexpr double default_time_limit = 10; constexpr bool default_heuristics_only = true; -TEST(termination_status, trivial_presolve_optimality_test) -{ - auto [termination_status, obj_val, lb] = test_mps_file( - "mip/trivial-presolve-optimality.mps", default_time_limit, default_heuristics_only); - EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); - EXPECT_EQ(obj_val, -1); -} - -TEST(termination_status, trivial_presolve_no_obj_vars_test) -{ - auto [termination_status, obj_val, lb] = test_mps_file( - "mip/trivial-presolve-no-obj-vars.mps", default_time_limit, default_heuristics_only); - EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); - EXPECT_EQ(obj_val, 0); -} - -TEST(termination_status, presolve_optimality_test) -{ - auto [termination_status, obj_val, lb] = - test_mps_file("mip/sudoku.mps", default_time_limit, default_heuristics_only); - EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); - EXPECT_EQ(obj_val, 0); -} - -TEST(termination_status, presolve_infeasible_test) -{ - auto [termination_status, obj_val, lb] = - test_mps_file("mip/presolve-infeasible.mps", default_time_limit, default_heuristics_only); - EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); -} +// TEST(termination_status, trivial_presolve_optimality_test) +//{ +// auto [termination_status, obj_val, lb] = test_mps_file( +// "mip/trivial-presolve-optimality.mps", default_time_limit, default_heuristics_only); +// EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); +// EXPECT_EQ(obj_val, -1); +// } +// +// TEST(termination_status, trivial_presolve_no_obj_vars_test) +//{ +// auto [termination_status, obj_val, lb] = test_mps_file( +// "mip/trivial-presolve-no-obj-vars.mps", default_time_limit, default_heuristics_only); +// EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); +// EXPECT_EQ(obj_val, 0); +// } +// +// TEST(termination_status, presolve_optimality_test) +//{ +// auto [termination_status, obj_val, lb] = +// test_mps_file("mip/sudoku.mps", default_time_limit, default_heuristics_only); +// EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); +// EXPECT_EQ(obj_val, 0); +// } +// +// TEST(termination_status, presolve_infeasible_test) +//{ +// auto [termination_status, obj_val, lb] = +// test_mps_file("mip/presolve-infeasible.mps", default_time_limit, default_heuristics_only); +// EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); +// } TEST(termination_status, feasible_found_test) { @@ -86,42 +86,42 @@ TEST(termination_status, feasible_found_test) EXPECT_EQ(termination_status, mip_termination_status_t::FeasibleFound); } -TEST(termination_status, timeout_test) -{ - auto [termination_status, obj_val, lb] = - test_mps_file("mip/stein9inf.mps", default_time_limit, default_heuristics_only); - EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit); -} - -TEST(termination_status, optimality_test) -{ - auto [termination_status, obj_val, lb] = - test_mps_file("mip/bb_optimality.mps", default_time_limit, false); - EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); - EXPECT_EQ(obj_val, 2); -} - -// Ensure the lower bound on maximization problems when BB times out has the right sign -TEST(termination_status, lower_bound_bb_timeout) -{ - auto [termination_status, obj_val, lb] = test_mps_file("mip/cod105_max.mps", 0.5, false); - EXPECT_EQ(termination_status, mip_termination_status_t::FeasibleFound); - EXPECT_EQ(obj_val, 12); - EXPECT_GE(lb, obj_val); -} - -TEST(termination_status, bb_infeasible_test) -{ - // First, check that presolve doesn't reduce the problem to infeasibility - { - auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", 1, true); - EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit); - } - // Ensure that B&B proves the MIP infeasible - { - auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", 30, false); - EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); - } -} +// TEST(termination_status, timeout_test) +//{ +// auto [termination_status, obj_val, lb] = +// test_mps_file("mip/stein9inf.mps", default_time_limit, default_heuristics_only); +// EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit); +// } +// +// TEST(termination_status, optimality_test) +//{ +// auto [termination_status, obj_val, lb] = +// test_mps_file("mip/bb_optimality.mps", default_time_limit, false); +// EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); +// EXPECT_EQ(obj_val, 2); +// } +// +//// Ensure the lower bound on maximization problems when BB times out has the right sign +// TEST(termination_status, lower_bound_bb_timeout) +//{ +// auto [termination_status, obj_val, lb] = test_mps_file("mip/cod105_max.mps", 0.5, false); +// EXPECT_EQ(termination_status, mip_termination_status_t::FeasibleFound); +// EXPECT_EQ(obj_val, 12); +// EXPECT_GE(lb, obj_val); +// } +// +// TEST(termination_status, bb_infeasible_test) +//{ +// // First, check that presolve doesn't reduce the problem to infeasibility +// { +// auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", 1, true); +// EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit); +// } +// // Ensure that B&B proves the MIP infeasible +// { +// auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", 30, false); +// EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); +// } +// } } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/unit_test.cu b/cpp/tests/mip/unit_test.cu index e87efd8605..ab82c85bed 100644 --- a/cpp/tests/mip/unit_test.cu +++ b/cpp/tests/mip/unit_test.cu @@ -132,39 +132,39 @@ mps_parser::mps_data_model_t create_single_var_milp_problem(bool ma return problem; } -TEST(LPTest, TestSampleLP) -{ - raft::handle_t handle; - auto problem = create_std_lp_problem(); - - cuopt::linear_programming::pdlp_solver_settings_t settings{}; - settings.set_optimality_tolerance(1e-4); - settings.time_limit = 5; - - auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); - - EXPECT_EQ(result.get_termination_status(), - cuopt::linear_programming::pdlp_termination_status_t::Optimal); -} - -TEST(ErrorTest, TestError) -{ - raft::handle_t handle; - auto problem = create_std_milp_problem(false); - - cuopt::linear_programming::mip_solver_settings_t settings{}; - settings.time_limit = 5; - // Set constraint bounds - std::vector lower_bounds = {1.0}; - std::vector upper_bounds = {0.0}; - problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); - problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); - - auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); - - EXPECT_EQ(result.get_termination_status(), - cuopt::linear_programming::mip_termination_status_t::NoTermination); -} +// TEST(LPTest, TestSampleLP) +//{ +// raft::handle_t handle; +// auto problem = create_std_lp_problem(); +// +// cuopt::linear_programming::pdlp_solver_settings_t settings{}; +// settings.set_optimality_tolerance(1e-4); +// settings.time_limit = 5; +// +// auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); +// +// EXPECT_EQ(result.get_termination_status(), +// cuopt::linear_programming::pdlp_termination_status_t::Optimal); +// } + +// TEST(ErrorTest, TestError) +//{ +// raft::handle_t handle; +// auto problem = create_std_milp_problem(false); +// +// cuopt::linear_programming::mip_solver_settings_t settings{}; +// settings.time_limit = 5; +// // Set constraint bounds +// std::vector lower_bounds = {1.0}; +// std::vector upper_bounds = {0.0}; +// problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); +// problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); +// +// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); +// +// EXPECT_EQ(result.get_termination_status(), +// cuopt::linear_programming::mip_termination_status_t::NoTermination); +// } class MILPTestParams : public testing::TestWithParam< @@ -190,37 +190,40 @@ TEST_P(MILPTestParams, TestSampleMILP) EXPECT_EQ(result.get_termination_status(), expected_termination_status); } -TEST_P(MILPTestParams, TestSingleVarMILP) -{ - bool maximize = std::get<0>(GetParam()); - bool scaling = std::get<1>(GetParam()); - bool heuristics_only = std::get<2>(GetParam()); - auto expected_termination_status = std::get<3>(GetParam()); - - raft::handle_t handle; - auto problem = create_single_var_milp_problem(maximize); - - cuopt::linear_programming::mip_solver_settings_t settings{}; - settings.time_limit = 5; - settings.mip_scaling = scaling; - settings.heuristics_only = heuristics_only; - - auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); - - EXPECT_EQ(result.get_termination_status(), - cuopt::linear_programming::mip_termination_status_t::Optimal); -} +// TEST_P(MILPTestParams, TestSingleVarMILP) +//{ +// bool maximize = std::get<0>(GetParam()); +// bool scaling = std::get<1>(GetParam()); +// bool heuristics_only = std::get<2>(GetParam()); +// auto expected_termination_status = std::get<3>(GetParam()); +// +// raft::handle_t handle; +// auto problem = create_single_var_milp_problem(maximize); +// +// cuopt::linear_programming::mip_solver_settings_t settings{}; +// settings.time_limit = 5; +// settings.mip_scaling = scaling; +// settings.heuristics_only = heuristics_only; +// +// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); +// +// EXPECT_EQ(result.get_termination_status(), +// cuopt::linear_programming::mip_termination_status_t::Optimal); +// } INSTANTIATE_TEST_SUITE_P( MILPTests, MILPTestParams, - testing::Values( - std::make_tuple(true, true, true, cuopt::linear_programming::mip_termination_status_t::Optimal), - std::make_tuple( - false, true, false, cuopt::linear_programming::mip_termination_status_t::Optimal), - std::make_tuple( - true, false, true, cuopt::linear_programming::mip_termination_status_t::Optimal), - std::make_tuple( - false, false, false, cuopt::linear_programming::mip_termination_status_t::Optimal))); + testing::Values(std::make_tuple( + true, true, true, cuopt::linear_programming::mip_termination_status_t::Optimal))); +// MILPTestParams, +// testing::Values( +// std::make_tuple(true, true, true, +// cuopt::linear_programming::mip_termination_status_t::Optimal), std::make_tuple( +// false, true, false, cuopt::linear_programming::mip_termination_status_t::Optimal), +// std::make_tuple( +// true, false, true, cuopt::linear_programming::mip_termination_status_t::Optimal), +// std::make_tuple( +// false, false, false, cuopt::linear_programming::mip_termination_status_t::Optimal))); } // namespace cuopt::linear_programming::test From d6e48ec392444c6beb19d7d3d90c513bb20b5b7e Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Thu, 4 Sep 2025 14:25:01 -0400 Subject: [PATCH 05/10] fixed --- cpp/src/mip/diversity/assignment_hash_map.cu | 24 ---- cpp/src/mip/diversity/diversity_manager.cu | 117 --------------- .../mip/feasibility_jump/feasibility_jump.cu | 71 +--------- cpp/src/mip/local_search/local_search.cu | 5 - .../local_search/rounding/constraint_prop.cu | 33 ----- cpp/src/mip/solution/solution.cu | 33 ----- cpp/src/mip/solver.cu | 34 +---- cpp/src/mip/utils.cuh | 2 - cpp/tests/mip/termination_test.cu | 134 +++++++++--------- cpp/tests/mip/unit_test.cu | 125 ++++++++-------- 10 files changed, 130 insertions(+), 448 deletions(-) diff --git a/cpp/src/mip/diversity/assignment_hash_map.cu b/cpp/src/mip/diversity/assignment_hash_map.cu index 803969fa86..24d0051b37 100644 --- a/cpp/src/mip/diversity/assignment_hash_map.cu +++ b/cpp/src/mip/diversity/assignment_hash_map.cu @@ -80,8 +80,6 @@ template void assignment_hash_map_t::fill_integer_assignment(solution_t& solution) { static_assert(sizeof(f_t) == sizeof(size_t), "f_t must be double precision"); - std::cerr << "integer_indices size " << solution.problem_ptr->integer_indices.size() - << " integer_assignment " << integer_assignment.size() << "\n"; thrust::gather(solution.handle_ptr->get_thrust_policy(), solution.problem_ptr->integer_indices.begin(), solution.problem_ptr->integer_indices.end(), @@ -94,26 +92,12 @@ size_t assignment_hash_map_t::hash_solution(solution_t& solu { const int TPB = 1024; - std::cerr << "has_solution pt 0\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - fill_integer_assignment(solution); - std::cerr << "has_solution pt 1\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - thrust::fill( solution.handle_ptr->get_thrust_policy(), reduction_buffer.begin(), reduction_buffer.end(), 0); hash_solution_kernel <<<(integer_assignment.size() + TPB - 1) / TPB, TPB, 0, solution.handle_ptr->get_stream()>>>( cuopt::make_span(integer_assignment), cuopt::make_span(reduction_buffer)); - std::cerr << "has_solution pt 2\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); // Get the number of blocks used in the hash_solution_kernel int num_blocks = (integer_assignment.size() + TPB - 1) / TPB; @@ -136,10 +120,6 @@ size_t assignment_hash_map_t::hash_solution(solution_t& solu temp_storage.resize(temp_storage_bytes, solution.handle_ptr->get_stream()); d_temp_storage = temp_storage.data(); - std::cerr << "has_solution pt 3\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); // Run reduction cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes, @@ -149,10 +129,6 @@ size_t assignment_hash_map_t::hash_solution(solution_t& solu combine_hash(), 0, solution.handle_ptr->get_stream()); - std::cerr << "has_solution pt 4\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); // Return early since we've already computed the hash sum return hash_sum.value(solution.handle_ptr->get_stream()); diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index df98274c82..d25eb9405f 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -114,34 +114,10 @@ bool diversity_manager_t::run_local_search(solution_t& solut { // i_t ls_mab_option = mab_ls.select_mab_option(); // mab_ls_config_t::get_local_search_and_lm_from_config(ls_mab_option, ls_config); - int dev; - cudaGetDevice(&dev); - std::cerr << "run_local_search pt -1\n device " << dev << "\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); assignment_hash_map.insert(solution); constexpr i_t skip_solutions_threshold = 3; - std::cerr << "run_local_search pt 0\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); if (assignment_hash_map.check_skip_solution(solution, skip_solutions_threshold)) { return false; } - std::cerr << "run_local_search pt 1\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); ls.run_local_search(solution, weights, timer, ls_config); - std::cerr << "run_local_search pt 2\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); return true; } @@ -330,19 +306,11 @@ bool diversity_manager_t::run_presolve(f_t time_limit) stats.presolve_time = timer.elapsed_time(); return false; } - problem_ptr->handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(problem_ptr->handle_ptr->get_stream()); - cudaDeviceSynchronize(); - std::cerr << "pt 0\n"; if (termination_criterion_t::NO_UPDATE != term_crit) { ls.constraint_prop.bounds_update.set_updated_bounds(*problem_ptr); trivial_presolve(*problem_ptr); if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; } } - problem_ptr->handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(problem_ptr->handle_ptr->get_stream()); - cudaDeviceSynchronize(); - std::cerr << "pt 1\n"; if (!problem_ptr->empty) { // do the resizing no-matter what, bounds presolve might not change the bounds but initial // trivial presolve might have @@ -351,66 +319,29 @@ bool diversity_manager_t::run_presolve(f_t time_limit) *problem_ptr, ls.constraint_prop.bounds_update); if (!check_bounds_sanity(*problem_ptr)) { return false; } } - problem_ptr->handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(problem_ptr->handle_ptr->get_stream()); - cudaDeviceSynchronize(); - std::cerr << "pt 2\n"; stats.presolve_time = presolve_timer.elapsed_time(); lp_optimal_solution.resize(problem_ptr->n_variables, problem_ptr->handle_ptr->get_stream()); - problem_ptr->handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(problem_ptr->handle_ptr->get_stream()); - cudaDeviceSynchronize(); - std::cerr << "pt 3\n"; return true; } template void diversity_manager_t::generate_quick_feasible_solution() { - std::cerr << "generate_fast_solution_time pt 0\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - solution_t solution(*problem_ptr); - std::cerr << "generate_fast_solution_time pt 1\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); // min 1 second, max 10 seconds const f_t generate_fast_solution_time = std::min(diversity_config_t::max_fast_sol_time, std::max(1., timer.remaining_time() / 20.)); timer_t sol_timer(generate_fast_solution_time); // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); - std::cerr << "generate_fast_solution_time pt 2\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); if (solution.get_feasible()) { population.run_solution_callbacks(solution); initial_sol_vector.emplace_back(std::move(solution)); problem_ptr->handle_ptr->sync_stream(); solution_t searched_sol(initial_sol_vector.back()); - std::cerr << "generate_fast_solution_time pt 2a\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); ls_config_t ls_config; - std::cerr << "generate_fast_solution_time pt 3\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); run_local_search(searched_sol, population.weights, sol_timer, ls_config); - std::cerr << "generate_fast_solution_time pt 4\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); population.run_solution_callbacks(searched_sol); - std::cerr << "generate_fast_solution_time pt 5\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); initial_sol_vector.emplace_back(std::move(searched_sol)); auto& feas_sol = initial_sol_vector.back().get_feasible() ? initial_sol_vector.back() @@ -462,20 +393,12 @@ solution_t diversity_manager_t::run_solver() cuopt::scope_guard([&]() { stats.total_solve_time = timer.elapsed_time(); }); // after every change to the problem, we should resize all the relevant vars // we need to encapsulate that to prevent repetitions - std::cerr << "run_solver pt 0\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); ls.resize_vectors(*problem_ptr, problem_ptr->handle_ptr); ls.constraint_prop.bounds_update.resize(*problem_ptr); problem_ptr->check_problem_representation(true); // have the structure ready for reusing later problem_ptr->compute_integer_fixed_problem(); - std::cerr << "run_solver pt 1\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); // test problem is not ii cuopt_func_call( @@ -484,21 +407,9 @@ solution_t diversity_manager_t::run_solver() ls.constraint_prop.bounds_update.calculate_infeasible_redundant_constraints(*problem_ptr), "The problem must not be ii"); population.initialize_population(); - std::cerr << "run_solver pt 2\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); if (check_b_b_preemption()) { return population.best_feasible(); } - std::cerr << "run_solver pt 2a\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); // before probing cache or LP, run FJ to generate initial primal feasible solution if (!from_dir) { generate_quick_feasible_solution(); } - std::cerr << "run_solver pt 2b\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); constexpr f_t time_ratio_of_probing_cache = diversity_config_t::time_ratio_of_probing_cache; constexpr f_t max_time_on_probing = diversity_config_t::max_time_on_probing; f_t time_for_probing_cache = @@ -506,20 +417,8 @@ solution_t diversity_manager_t::run_solver() timer_t probing_timer{time_for_probing_cache}; if (check_b_b_preemption()) { return population.best_feasible(); } if (!fj_only_run) { - std::cerr << "run_solver pt 2c\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); - std::cerr << "run_solver pt 2d\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); } - std::cerr << "run_solver pt 3\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); if (check_b_b_preemption()) { return population.best_feasible(); } lp_state_t& lp_state = problem_ptr->lp_state; @@ -571,19 +470,11 @@ solution_t diversity_manager_t::run_solver() // in case the pdlp returned var boudns that are out of bounds clamp_within_var_bounds(lp_optimal_solution, problem_ptr, problem_ptr->handle_ptr); } - std::cerr << "run_solver pt 4\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); population.allocate_solutions(); if (check_b_b_preemption()) { return population.best_feasible(); } // generate a population with 5 solutions(FP+FJ) generate_initial_solutions(); - std::cerr << "run_solver pt 5\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); if (context.settings.benchmark_info_ptr != nullptr) { context.settings.benchmark_info_ptr->objective_of_initial_population = @@ -596,16 +487,8 @@ solution_t diversity_manager_t::run_solver() } if (timer.check_time_limit()) { return population.best_feasible(); } - std::cerr << "run_solver pt 6\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); main_loop(); - std::cerr << "run_solver pt 7\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); return population.best_feasible(); }; diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index ad06ca92d2..cbe3e0e9f0 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -827,10 +827,6 @@ void fj_t::refresh_lhs_and_violation(const rmm::cuda_stream_view& stre template i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) { - std::cerr << "host_loop pt 0\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); auto& data = *climbers[climber_idx]; auto v = data.view(); // == climber_views[climber_idx] @@ -839,10 +835,6 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; solution.compute_feasibility(); - std::cerr << "host_loop pt 1\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); if (settings.feasibility_run) { objective_weight.set_value_to_zero_async(handle_ptr->get_stream()); } @@ -877,25 +869,13 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) objective_weight.value(climber_stream)); } - std::cerr << "host_loop pt 2\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - if (!limit_reached) { run_step_device(climber_stream, climber_idx, false); } - std::cerr << "host_loop pt 3\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); + if (!limit_reached) { run_step_device(climber_stream, climber_idx); } // periodically recompute the LHS and violation scores // to correct any accumulated numerical errors if (steps % settings.parameters.lhs_refresh_period == 0) { refresh_lhs_and_violation(climber_stream, climber_idx); } - std::cerr << "host_loop pt 4\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); // periodically synchronize and check the latest solution // feasible solution found!*view.break_condition @@ -908,10 +888,6 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) i_t iterations = data.iterations.value(climber_stream); // make sure we have the current incumbent saved (e.g. in the case of a timeout) update_best_solution_kernel<<<1, blocks_resetmoves, 0, climber_stream>>>(v); - std::cerr << "host_loop pt 5\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); // check feasibility with the relative tolerance rather than the violation score raft::copy(solution.assignment.data(), data.best_assignment.data(), @@ -921,15 +897,7 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) // this solution cost computation with the changing(or not changing) weights is needed to // decide whether we reset the best objective on the FIRST_FEASIBLE mode. once we get rid of // FIRST_FEASIBLE mode, we can remove the following too. - std::cerr << "host_loop pt 6\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); bool is_feasible = solution.compute_feasibility(); - std::cerr << "host_loop pt 7\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); solution.handle_ptr->sync_stream(); if (limit_reached) { break; } @@ -1067,11 +1035,6 @@ i_t fj_t::solve(solution_t& solution) pb_ptr->check_problem_representation(true); resize_vectors(solution.handle_ptr); - std::cerr << "fj solve pt 0\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - bool is_initial_feasible = solution.compute_feasibility(); // if we're in rounding mode, split the time/iteration limit between the first and second stage cuopt_assert(settings.parameters.rounding_second_stage_split >= 0 && @@ -1101,29 +1064,14 @@ i_t fj_t::solve(solution_t& solution) handle_ptr->get_stream()); } - std::cerr << "fj solve pt 1\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - climber_init(0); RAFT_CHECK_CUDA(handle_ptr->get_stream()); handle_ptr->sync_stream(); - std::cerr << "fj solve pt 2\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - i_t iterations = host_loop(solution); RAFT_CHECK_CUDA(handle_ptr->get_stream()); handle_ptr->sync_stream(); - std::cerr << "fj solve pt 3\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - f_t effort_rate = (f_t)iterations / timer.elapsed_time(); // If we're in rounding mode and some fractionals remain: round them all @@ -1135,24 +1083,12 @@ i_t fj_t::solve(solution_t& solution) settings.iteration_limit * settings.parameters.rounding_second_stage_split; round_remaining_fractionals(solution); - std::cerr << "fj solve pt 4\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); // if time limit exceeded: round all remaining fractionals if any by nearest rounding. if (climbers[0]->fractional_variables.set_size.value(handle_ptr->get_stream()) > 0) { solution.round_nearest(); - std::cerr << "fj solve pt 5\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); } } - std::cerr << "fj solve pt 6\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); CUOPT_LOG_TRACE("GPU solver took %g", timer.elapsed_time()); CUOPT_LOG_TRACE("limit reached, effort rate %g steps/secm %d steps", effort_rate, iterations); @@ -1171,11 +1107,6 @@ i_t fj_t::solve(solution_t& solution) cuopt_assert(solution.test_number_all_integer(), "All integers must be rounded"); bool is_new_feasible = solution.compute_feasibility(); - std::cerr << "fj solve pt 7\n"; - solution.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(solution.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - if (is_initial_feasible && !is_new_feasible) { CUOPT_LOG_ERROR( "Feasibility jump caused feasible solution to become infeasible\n" diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 8608a6c05a..a2eda273b3 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -67,15 +67,12 @@ void local_search_t::generate_fast_solution(solution_t& solu while (!timer.check_time_limit()) { timer_t constr_prop_timer = timer_t(std::min(timer.remaining_time(), 2.)); // do constraint prop on lp optimal solution - std::cerr << "apply round\n"; 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()); // run fj on the solution - std::cerr << "solve\n"; fj.solve(solution); - std::cerr << "solve done\n"; // TODO check if FJ returns the same solution // check if the solution is feasible if (solution.compute_feasibility()) { return; } @@ -115,10 +112,8 @@ bool local_search_t::run_local_search(solution_t& solution, rd = ls_method_t::FJ_ANNEALING; } if (rd == ls_method_t::FJ_LINE_SEGMENT && lp_optimal_exists) { - std::cerr << "run_local_search pt 0\n"; is_feas = run_fj_line_segment(solution, timer, ls_config); } else { - std::cerr << "run_local_search pt 1\n"; is_feas = run_fj_annealing(solution, timer, ls_config); } return is_feas; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 745feccca1..efe7776776 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -919,9 +919,6 @@ bool constraint_prop_t::find_integer( multi_probe.settings.iteration_limit = 50; multi_probe.settings.time_limit = max_timer.remaining_time(); multi_probe.resize(*sol.problem_ptr); - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer pt 0\n"; if (max_timer.check_time_limit()) { CUOPT_LOG_DEBUG("Time limit is reached before bounds prop rounding!"); sol.round_nearest(); @@ -929,9 +926,6 @@ bool constraint_prop_t::find_integer( cuopt_func_call(orig_sol.test_variable_bounds()); return orig_sol.compute_feasibility(); } - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer pt 1\n"; raft::copy(unset_integer_vars.data(), sol.problem_ptr->integer_indices.data(), sol.problem_ptr->n_integer_vars, @@ -943,24 +937,12 @@ bool constraint_prop_t::find_integer( cuopt_func_call(orig_sol.test_variable_bounds()); return orig_sol.compute_feasibility(); } - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer pt 2\n"; // this is needed for the sort inside of the loop bool problem_ii = is_problem_ii(*sol.problem_ptr); - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer pt 2a\n"; // if the problem is ii, run the bounds prop in the beginning if (problem_ii) { - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer pt 3\n"; bool bounds_repaired = bounds_repair.repair_problem(*sol.problem_ptr, *orig_sol.problem_ptr, timer, sol.handle_ptr); - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer pt 4\n"; if (bounds_repaired) { CUOPT_LOG_DEBUG("Initial ii is repaired by bounds repair!"); } else { @@ -970,23 +952,14 @@ bool constraint_prop_t::find_integer( } rounding_ii = true; } - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer pt 5\n"; } // do the sort if the problem is not ii. crossing bounds might cause some issues on the sort order else { // this is a sort to have initial shuffling, so that stable sort within will keep the order and // some randomness will be achieved sort_by_interval_and_frac(sol, make_span(unset_integer_vars), rng); - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer pt 6\n"; } set_host_bounds(sol); - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer pt 7\n"; size_t set_count = 0; bool timeout_happened = false; i_t n_failed_repair_iterations = 0; @@ -1136,13 +1109,7 @@ bool constraint_prop_t::apply_round( temp_sol.problem_ptr = &p; f_t bounds_prop_start_time = max_timer.remaining_time(); cuopt_func_call(temp_sol.test_variable_bounds(false)); - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer\n"; bool sol_found = find_integer(temp_sol, sol, lp_run_time_after_feasible, timer, probing_config); - sol.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(sol.handle_ptr->get_stream()); - std::cerr << "find_integer done\n"; f_t bounds_prop_end_time = max_timer.remaining_time(); repair_stats.total_time_spent_on_bounds_prop += bounds_prop_start_time - bounds_prop_end_time; diff --git a/cpp/src/mip/solution/solution.cu b/cpp/src/mip/solution/solution.cu index 929ba2b810..6f1ba94c72 100644 --- a/cpp/src/mip/solution/solution.cu +++ b/cpp/src/mip/solution/solution.cu @@ -319,31 +319,10 @@ template bool solution_t::compute_feasibility() { n_feasible_constraints.set_value_to_zero_async(handle_ptr->get_stream()); - std::cerr << "compute_feasibility pt 0\n"; - handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(handle_ptr->get_stream()); - cudaDeviceSynchronize(); compute_constraints(); - std::cerr << "compute_feasibility pt 1\n"; - handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(handle_ptr->get_stream()); - cudaDeviceSynchronize(); - std::cerr << "compute_feasibility pt 1a\n"; compute_objective(); - std::cerr << "compute_feasibility pt 2\n"; - handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(handle_ptr->get_stream()); - cudaDeviceSynchronize(); compute_infeasibility(); - std::cerr << "compute_feasibility pt 3\n"; - handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(handle_ptr->get_stream()); - cudaDeviceSynchronize(); compute_number_of_integers(); - std::cerr << "compute_feasibility pt 4\n"; - handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(handle_ptr->get_stream()); - cudaDeviceSynchronize(); i_t h_n_feas_constraints = n_feasible_constraints.value(handle_ptr->get_stream()); is_feasible = h_n_feas_constraints == problem_ptr->n_constraints && test_number_all_integer(); CUOPT_LOG_TRACE("is_feasible %d n_feasible_cstr %d all_cstr %d", @@ -356,23 +335,11 @@ bool solution_t::compute_feasibility() template void solution_t::compute_objective() { - std::cerr << "compute_objective pt 0\n"; - handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(handle_ptr->get_stream()); - cudaDeviceSynchronize(); h_obj = compute_objective_from_vec( assignment, problem_ptr->objective_coefficients, handle_ptr); - std::cerr << "compute_objective pt 1\n"; - handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(handle_ptr->get_stream()); - cudaDeviceSynchronize(); // to save from memory transactions, don't update the device objective // when needed we can update the device objective here h_user_obj = problem_ptr->get_user_obj_from_solver_obj(h_obj); - std::cerr << "compute_objective pt 2\n"; - handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(handle_ptr->get_stream()); - cudaDeviceSynchronize(); } template diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index f7a83e5ddc..0f2117991f 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -148,11 +148,6 @@ solution_t mip_solver_t::run_solver() return sol; } - std::cerr << "pt 4\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - namespace dual_simplex = cuopt::linear_programming::dual_simplex; std::future branch_and_bound_status_future; dual_simplex::user_problem_t branch_and_bound_problem; @@ -161,19 +156,9 @@ solution_t mip_solver_t::run_solver() branch_and_bound_solution_helper_t solution_helper(&dm, branch_and_bound_settings); dual_simplex::mip_solution_t branch_and_bound_solution(1); - std::cerr << "pt 5\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - if (!context.settings.heuristics_only) { // Convert the presolved problem to dual_simplex::user_problem_t op_problem_.get_host_user_problem(branch_and_bound_problem); - std::cerr << "pt 6\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - // Resize the solution now that we know the number of columns/variables branch_and_bound_solution.resize(branch_and_bound_problem.num_cols); @@ -222,18 +207,9 @@ solution_t mip_solver_t::run_solver() branch_and_bound.get(), std::ref(branch_and_bound_solution)); } - std::cerr << "pt 7\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); - std::cerr << "pt 7a\n"; // Start the primal heuristics auto sol = dm.run_solver(); - std::cerr << "pt 8\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); if (!context.settings.heuristics_only) { // Wait for the branch and bound to finish auto bb_status = branch_and_bound_status_future.get(); @@ -245,17 +221,9 @@ solution_t mip_solver_t::run_solver() context.stats.num_nodes = branch_and_bound_solution.nodes_explored; context.stats.num_simplex_iterations = branch_and_bound_solution.simplex_iterations; } - std::cerr << "pt 9\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); sol.compute_feasibility(); - rmm::device_scalar is_feasible(context.handle_ptr->get_stream()); + rmm::device_scalar is_feasible(sol.handle_ptr->get_stream()); sol.test_variable_bounds(true, is_feasible.data()); - std::cerr << "pt 10\n"; - context.handle_ptr->sync_stream(); - RAFT_CHECK_CUDA(context.handle_ptr->get_stream()); - cudaDeviceSynchronize(); // test_variable_bounds clears is_feasible if the test is failed if (!is_feasible.value(sol.handle_ptr->get_stream())) { CUOPT_LOG_ERROR( diff --git a/cpp/src/mip/utils.cuh b/cpp/src/mip/utils.cuh index c24e5928eb..734cf10b6b 100644 --- a/cpp/src/mip/utils.cuh +++ b/cpp/src/mip/utils.cuh @@ -224,8 +224,6 @@ f_t compute_objective_from_vec(const rmm::device_uvector& assignment, const raft::handle_t* handle_ptr) { cuopt_assert(assignment.size() == objective_coefficients.size(), "Size mismatch!"); - std::cerr << "assignment " << assignment.size() << "\n"; - std::cerr << "objective_coefficients " << objective_coefficients.size() << "\n"; f_t computed_obj = thrust::inner_product(handle_ptr->get_thrust_policy(), assignment.begin(), assignment.end(), diff --git a/cpp/tests/mip/termination_test.cu b/cpp/tests/mip/termination_test.cu index a33a1d75e4..54be62c591 100644 --- a/cpp/tests/mip/termination_test.cu +++ b/cpp/tests/mip/termination_test.cu @@ -48,36 +48,36 @@ namespace cuopt::linear_programming::test { constexpr double default_time_limit = 10; constexpr bool default_heuristics_only = true; -// TEST(termination_status, trivial_presolve_optimality_test) -//{ -// auto [termination_status, obj_val, lb] = test_mps_file( -// "mip/trivial-presolve-optimality.mps", default_time_limit, default_heuristics_only); -// EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); -// EXPECT_EQ(obj_val, -1); -// } -// -// TEST(termination_status, trivial_presolve_no_obj_vars_test) -//{ -// auto [termination_status, obj_val, lb] = test_mps_file( -// "mip/trivial-presolve-no-obj-vars.mps", default_time_limit, default_heuristics_only); -// EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); -// EXPECT_EQ(obj_val, 0); -// } -// -// TEST(termination_status, presolve_optimality_test) -//{ -// auto [termination_status, obj_val, lb] = -// test_mps_file("mip/sudoku.mps", default_time_limit, default_heuristics_only); -// EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); -// EXPECT_EQ(obj_val, 0); -// } -// -// TEST(termination_status, presolve_infeasible_test) -//{ -// auto [termination_status, obj_val, lb] = -// test_mps_file("mip/presolve-infeasible.mps", default_time_limit, default_heuristics_only); -// EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); -// } +TEST(termination_status, trivial_presolve_optimality_test) +{ + auto [termination_status, obj_val, lb] = test_mps_file( + "mip/trivial-presolve-optimality.mps", default_time_limit, default_heuristics_only); + EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); + EXPECT_EQ(obj_val, -1); +} + +TEST(termination_status, trivial_presolve_no_obj_vars_test) +{ + auto [termination_status, obj_val, lb] = test_mps_file( + "mip/trivial-presolve-no-obj-vars.mps", default_time_limit, default_heuristics_only); + EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); + EXPECT_EQ(obj_val, 0); +} + +TEST(termination_status, presolve_optimality_test) +{ + auto [termination_status, obj_val, lb] = + test_mps_file("mip/sudoku.mps", default_time_limit, default_heuristics_only); + EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); + EXPECT_EQ(obj_val, 0); +} + +TEST(termination_status, presolve_infeasible_test) +{ + auto [termination_status, obj_val, lb] = + test_mps_file("mip/presolve-infeasible.mps", default_time_limit, default_heuristics_only); + EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); +} TEST(termination_status, feasible_found_test) { @@ -86,42 +86,42 @@ TEST(termination_status, feasible_found_test) EXPECT_EQ(termination_status, mip_termination_status_t::FeasibleFound); } -// TEST(termination_status, timeout_test) -//{ -// auto [termination_status, obj_val, lb] = -// test_mps_file("mip/stein9inf.mps", default_time_limit, default_heuristics_only); -// EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit); -// } -// -// TEST(termination_status, optimality_test) -//{ -// auto [termination_status, obj_val, lb] = -// test_mps_file("mip/bb_optimality.mps", default_time_limit, false); -// EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); -// EXPECT_EQ(obj_val, 2); -// } -// -//// Ensure the lower bound on maximization problems when BB times out has the right sign -// TEST(termination_status, lower_bound_bb_timeout) -//{ -// auto [termination_status, obj_val, lb] = test_mps_file("mip/cod105_max.mps", 0.5, false); -// EXPECT_EQ(termination_status, mip_termination_status_t::FeasibleFound); -// EXPECT_EQ(obj_val, 12); -// EXPECT_GE(lb, obj_val); -// } -// -// TEST(termination_status, bb_infeasible_test) -//{ -// // First, check that presolve doesn't reduce the problem to infeasibility -// { -// auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", 1, true); -// EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit); -// } -// // Ensure that B&B proves the MIP infeasible -// { -// auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", 30, false); -// EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); -// } -// } +TEST(termination_status, timeout_test) +{ + auto [termination_status, obj_val, lb] = + test_mps_file("mip/stein9inf.mps", default_time_limit, default_heuristics_only); + EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit); +} + +TEST(termination_status, optimality_test) +{ + auto [termination_status, obj_val, lb] = + test_mps_file("mip/bb_optimality.mps", default_time_limit, false); + EXPECT_EQ(termination_status, mip_termination_status_t::Optimal); + EXPECT_EQ(obj_val, 2); +} + +// Ensure the lower bound on maximization problems when BB times out has the right sign +TEST(termination_status, lower_bound_bb_timeout) +{ + auto [termination_status, obj_val, lb] = test_mps_file("mip/cod105_max.mps", 0.5, false); + EXPECT_EQ(termination_status, mip_termination_status_t::FeasibleFound); + EXPECT_EQ(obj_val, 12); + EXPECT_GE(lb, obj_val); +} + +TEST(termination_status, bb_infeasible_test) +{ + // First, check that presolve doesn't reduce the problem to infeasibility + { + auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", 1, true); + EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit); + } + // Ensure that B&B proves the MIP infeasible + { + auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", 30, false); + EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible); + } +} } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/unit_test.cu b/cpp/tests/mip/unit_test.cu index ab82c85bed..e87efd8605 100644 --- a/cpp/tests/mip/unit_test.cu +++ b/cpp/tests/mip/unit_test.cu @@ -132,39 +132,39 @@ mps_parser::mps_data_model_t create_single_var_milp_problem(bool ma return problem; } -// TEST(LPTest, TestSampleLP) -//{ -// raft::handle_t handle; -// auto problem = create_std_lp_problem(); -// -// cuopt::linear_programming::pdlp_solver_settings_t settings{}; -// settings.set_optimality_tolerance(1e-4); -// settings.time_limit = 5; -// -// auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); -// -// EXPECT_EQ(result.get_termination_status(), -// cuopt::linear_programming::pdlp_termination_status_t::Optimal); -// } - -// TEST(ErrorTest, TestError) -//{ -// raft::handle_t handle; -// auto problem = create_std_milp_problem(false); -// -// cuopt::linear_programming::mip_solver_settings_t settings{}; -// settings.time_limit = 5; -// // Set constraint bounds -// std::vector lower_bounds = {1.0}; -// std::vector upper_bounds = {0.0}; -// problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); -// problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); -// -// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); -// -// EXPECT_EQ(result.get_termination_status(), -// cuopt::linear_programming::mip_termination_status_t::NoTermination); -// } +TEST(LPTest, TestSampleLP) +{ + raft::handle_t handle; + auto problem = create_std_lp_problem(); + + cuopt::linear_programming::pdlp_solver_settings_t settings{}; + settings.set_optimality_tolerance(1e-4); + settings.time_limit = 5; + + auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); + + EXPECT_EQ(result.get_termination_status(), + cuopt::linear_programming::pdlp_termination_status_t::Optimal); +} + +TEST(ErrorTest, TestError) +{ + raft::handle_t handle; + auto problem = create_std_milp_problem(false); + + cuopt::linear_programming::mip_solver_settings_t settings{}; + settings.time_limit = 5; + // Set constraint bounds + std::vector lower_bounds = {1.0}; + std::vector upper_bounds = {0.0}; + problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); + problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); + + auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + + EXPECT_EQ(result.get_termination_status(), + cuopt::linear_programming::mip_termination_status_t::NoTermination); +} class MILPTestParams : public testing::TestWithParam< @@ -190,40 +190,37 @@ TEST_P(MILPTestParams, TestSampleMILP) EXPECT_EQ(result.get_termination_status(), expected_termination_status); } -// TEST_P(MILPTestParams, TestSingleVarMILP) -//{ -// bool maximize = std::get<0>(GetParam()); -// bool scaling = std::get<1>(GetParam()); -// bool heuristics_only = std::get<2>(GetParam()); -// auto expected_termination_status = std::get<3>(GetParam()); -// -// raft::handle_t handle; -// auto problem = create_single_var_milp_problem(maximize); -// -// cuopt::linear_programming::mip_solver_settings_t settings{}; -// settings.time_limit = 5; -// settings.mip_scaling = scaling; -// settings.heuristics_only = heuristics_only; -// -// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); -// -// EXPECT_EQ(result.get_termination_status(), -// cuopt::linear_programming::mip_termination_status_t::Optimal); -// } +TEST_P(MILPTestParams, TestSingleVarMILP) +{ + bool maximize = std::get<0>(GetParam()); + bool scaling = std::get<1>(GetParam()); + bool heuristics_only = std::get<2>(GetParam()); + auto expected_termination_status = std::get<3>(GetParam()); + + raft::handle_t handle; + auto problem = create_single_var_milp_problem(maximize); + + cuopt::linear_programming::mip_solver_settings_t settings{}; + settings.time_limit = 5; + settings.mip_scaling = scaling; + settings.heuristics_only = heuristics_only; + + auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + + EXPECT_EQ(result.get_termination_status(), + cuopt::linear_programming::mip_termination_status_t::Optimal); +} INSTANTIATE_TEST_SUITE_P( MILPTests, MILPTestParams, - testing::Values(std::make_tuple( - true, true, true, cuopt::linear_programming::mip_termination_status_t::Optimal))); -// MILPTestParams, -// testing::Values( -// std::make_tuple(true, true, true, -// cuopt::linear_programming::mip_termination_status_t::Optimal), std::make_tuple( -// false, true, false, cuopt::linear_programming::mip_termination_status_t::Optimal), -// std::make_tuple( -// true, false, true, cuopt::linear_programming::mip_termination_status_t::Optimal), -// std::make_tuple( -// false, false, false, cuopt::linear_programming::mip_termination_status_t::Optimal))); + testing::Values( + std::make_tuple(true, true, true, cuopt::linear_programming::mip_termination_status_t::Optimal), + std::make_tuple( + false, true, false, cuopt::linear_programming::mip_termination_status_t::Optimal), + std::make_tuple( + true, false, true, cuopt::linear_programming::mip_termination_status_t::Optimal), + std::make_tuple( + false, false, false, cuopt::linear_programming::mip_termination_status_t::Optimal))); } // namespace cuopt::linear_programming::test From 96eaf2483f937dd392d0f933aa5a5cce7301ee47 Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Thu, 4 Sep 2025 14:52:22 -0400 Subject: [PATCH 06/10] remove dead code --- .../restart_strategy/pdlp_restart_strategy.cu | 14 ---- .../convergence_information.cu | 12 ---- .../infeasibility_information.cu | 7 -- .../feasibility_pump/feasibility_pump.cu | 10 ++- .../local_search/rounding/constraint_prop.cu | 66 ++--------------- cpp/src/mip/presolve/multi_probe.cu | 2 - cpp/src/mip/problem/problem.cu | 70 +------------------ cpp/src/mip/problem/problem.cuh | 2 - 8 files changed, 11 insertions(+), 172 deletions(-) diff --git a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu index de2e37584a..759179066b 100644 --- a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu +++ b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu @@ -1459,13 +1459,6 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( // component becomes fixed by its bounds // Copying primal / dual bound before sorting them according to threshold - // TODO : transform - // raft::copy( - // lower_bound_.data(), problem_ptr->variable_lower_bounds.data(), primal_size_h_, - // stream_view_); - // raft::copy( - // upper_bound_.data(), problem_ptr->variable_upper_bounds.data(), primal_size_h_, - // stream_view_); using f_t2 = typename type_2::type; cub::DeviceTransform::Transform( problem_ptr->variable_bounds.data(), @@ -1650,13 +1643,6 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( a_add_scalar_times_b(target_threshold_.data()), stream_view_); // project by max(min(x[i], upperbound[i]),lowerbound[i]) for primal part - // raft::linalg::ternaryOp(duality_gap.primal_solution_tr_.data(), - // duality_gap.primal_solution_tr_.data(), - // problem_ptr->variable_lower_bounds.data(), - // problem_ptr->variable_upper_bounds.data(), - // primal_size_h_, - // clamp(), - // stream_view_); using f_t2 = typename type_2::type; cub::DeviceTransform::Transform(cuda::std::make_tuple(duality_gap.primal_solution_tr_.data(), problem_ptr->variable_bounds.data()), diff --git a/cpp/src/linear_programming/termination_strategy/convergence_information.cu b/cpp/src/linear_programming/termination_strategy/convergence_information.cu index e69c8abd98..ce579e3104 100644 --- a/cpp/src/linear_programming/termination_strategy/convergence_information.cu +++ b/cpp/src/linear_programming/termination_strategy/convergence_information.cu @@ -372,12 +372,6 @@ void convergence_information_t::compute_reduced_cost_from_primal_gradi raft::common::nvtx::range fun_scope("compute_reduced_cost_from_primal_gradient"); using f_t2 = typename type_2::type; - // raft::linalg::binaryOp(bound_value_.data(), - // primal_gradient.data(), - // problem_ptr->variable_bounds.data(), - // primal_size_h_, - // bound_value_gradient(), - // stream_view_); cub::DeviceTransform::Transform( cuda::std::make_tuple(primal_gradient.data(), problem_ptr->variable_bounds.data()), bound_value_.data(), @@ -411,12 +405,6 @@ void convergence_information_t::compute_reduced_costs_dual_objective_c using f_t2 = typename type_2::type; // if reduced cost is positive -> lower bound, negative -> upper bounds, 0 -> 0 // if bound_val is not finite let element be -inf, otherwise bound_value*reduced_cost - // raft::linalg::binaryOp(bound_value_.data(), - // reduced_cost_.data(), - // problem_ptr->variable_bounds.data(), - // primal_size_h_, - // bound_value_reduced_cost_product(), - // stream_view_); cub::DeviceTransform::Transform( cuda::std::make_tuple(reduced_cost_.data(), problem_ptr->variable_bounds.data()), bound_value_.data(), diff --git a/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu b/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu index 97a2235b5a..1e1bf3abf2 100644 --- a/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu +++ b/cpp/src/linear_programming/termination_strategy/infeasibility_information.cu @@ -363,13 +363,6 @@ void infeasibility_information_t::compute_reduced_cost_from_primal_gra rmm::device_uvector& primal_gradient, rmm::device_uvector& primal_ray) { using f_t2 = typename type_2::type; - // raft::linalg::binaryOp(bound_value_.data(), - // primal_gradient.data(), - // problem_ptr->variable_bounds.data(), - // //problem_ptr->variable_upper_bounds.data(), - // primal_size_h_, - // bound_value_gradient(), - // stream_view_); cub::DeviceTransform::Transform( cuda::std::make_tuple(primal_gradient.data(), problem_ptr->variable_bounds.data()), bound_value_.data(), diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index e38edf4d30..1d5865f42d 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -449,12 +449,10 @@ void feasibility_pump_t::relax_general_integers(solution_t& thrust::make_counting_iterator(solution.problem_ptr->n_variables), [var_types, var_bnds, copy_types, pb = solution.problem_ptr->view()] __device__(auto v_idx) { auto orig_v_type = var_types[v_idx]; - // auto lb = var_lb[v_idx]; - // auto ub = var_ub[v_idx]; - auto var_bounds = var_bnds[v_idx]; - auto lb = var_bounds.x; - auto ub = var_bounds.y; - bool var_binary = (pb.integer_equal(lb, 0) && pb.integer_equal(ub, 1)); + auto var_bounds = var_bnds[v_idx]; + auto lb = var_bounds.x; + auto ub = var_bounds.y; + bool var_binary = (pb.integer_equal(lb, 0) && pb.integer_equal(ub, 1)); auto copy_type = (orig_v_type == var_t::INTEGER) && var_binary ? var_t::INTEGER : var_t::CONTINUOUS; var_types[v_idx] = copy_type; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index efe7776776..ab86a2a87e 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -130,11 +130,9 @@ __global__ void compute_implied_slack_consumption_per_var( i_t var_offset = pb.reverse_offsets[var_idx]; i_t var_degree = pb.reverse_offsets[var_idx + 1] - var_offset; f_t th_var_implied_slack_consumption = 0.; - // f_t lb = pb.variable_lower_bounds[var_idx]; - // f_t ub = pb.variable_upper_bounds[var_idx]; - auto var_bnd = pb.variable_bounds[var_idx]; - f_t lb = var_bnd.x; - f_t ub = var_bnd.y; + auto var_bnd = pb.variable_bounds[var_idx]; + f_t lb = var_bnd.x; + f_t ub = var_bnd.y; for (i_t i = threadIdx.x; i < var_degree; i += blockDim.x) { auto a = pb.reverse_coefficients[var_offset + i]; auto cnst_idx = pb.reverse_constraints[var_offset + i]; @@ -395,11 +393,6 @@ void constraint_prop_t::collapse_crossing_bounds(problem_t& problem_t& orig_problem, const raft::handle_t* handle_ptr) { - // auto lb = make_span(problem.variable_lower_bounds); - // auto ub = make_span(problem.variable_upper_bounds); - // auto original_lb = make_span(orig_problem.variable_lower_bounds); - // auto original_ub = make_span(orig_problem.variable_upper_bounds); - auto v_bnds = make_span(problem.variable_bounds); auto original_v_bnds = make_span(orig_problem.variable_bounds); thrust::for_each( @@ -438,9 +431,7 @@ void constraint_prop_t::collapse_crossing_bounds(problem_t& template void constraint_prop_t::set_bounds_on_fixed_vars(solution_t& sol) { - auto assgn = make_span(sol.assignment); - // auto lb = make_span(sol.problem_ptr->variable_lower_bounds); - // auto ub = make_span(sol.problem_ptr->variable_upper_bounds); + auto assgn = make_span(sol.assignment); auto var_bounds = make_span(sol.problem_ptr->variable_bounds); thrust::for_each(sol.handle_ptr->get_thrust_policy(), sol.problem_ptr->integer_indices.begin(), @@ -449,8 +440,6 @@ void constraint_prop_t::set_bounds_on_fixed_vars(solution_t& auto var_val = assgn[idx]; if (pb.is_integer(var_val)) { var_bounds[idx] = typename type_2::type{var_val, var_val}; - // lb[idx] = var_val; - // ub[idx] = var_val; } }); } @@ -582,13 +571,6 @@ void constraint_prop_t::copy_bounds(rmm::device_uvector& output_l template void constraint_prop_t::save_bounds(solution_t& sol) { - // copy_bounds(lb_restore, - // ub_restore, - // assignment_restore, - // sol.problem_ptr->variable_lower_bounds, - // sol.problem_ptr->variable_upper_bounds, - // sol.assignment, - // sol.handle_ptr); copy_bounds(lb_restore, ub_restore, sol.problem_ptr->variable_bounds, sol.handle_ptr); raft::copy(assignment_restore.data(), sol.assignment.data(), @@ -599,13 +581,6 @@ void constraint_prop_t::save_bounds(solution_t& sol) template void constraint_prop_t::restore_bounds(solution_t& sol) { - // copy_bounds(sol.problem_ptr->variable_lower_bounds, - // sol.problem_ptr->variable_upper_bounds, - // sol.assignment, - // lb_restore, - // ub_restore, - // assignment_restore, - // sol.handle_ptr); copy_bounds(sol.problem_ptr->variable_bounds, lb_restore, ub_restore, sol.handle_ptr); raft::copy(sol.assignment.data(), assignment_restore.data(), @@ -617,13 +592,6 @@ template void constraint_prop_t::restore_original_bounds(solution_t& sol, solution_t& orig_sol) { - // copy_bounds(sol.problem_ptr->variable_lower_bounds, - // sol.problem_ptr->variable_upper_bounds, - // sol.assignment, - // orig_sol.problem_ptr->variable_lower_bounds, - // orig_sol.problem_ptr->variable_upper_bounds, - // orig_sol.assignment, - // orig_sol.handle_ptr); raft::copy(sol.problem_ptr->variable_bounds.data(), orig_sol.problem_ptr->variable_bounds.data(), orig_sol.problem_ptr->variable_bounds.size(), @@ -731,16 +699,6 @@ constraint_prop_t::generate_bulk_rounding_vector( cuopt_assert( test_var_out_of_bounds(orig_sol, unset_var_idx, second_probe, int_tol, sol.handle_ptr), "Variable out of original bounds!"); - // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( - // unset_var_idx, sol.handle_ptr->get_stream()) <= first_probe + int_tol && - // first_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( - // unset_var_idx, sol.handle_ptr->get_stream()), - // "Variable out of original bounds!"); - // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( - // unset_var_idx, sol.handle_ptr->get_stream()) <= second_probe + int_tol && - // second_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( - // unset_var_idx, sol.handle_ptr->get_stream()), - // "Variable out of original bounds!"); cuopt_assert(orig_sol.problem_ptr->is_integer(first_probe), "Probing value must be an integer"); cuopt_assert(orig_sol.problem_ptr->is_integer(second_probe), "Probing value must be an integer"); @@ -764,16 +722,6 @@ constraint_prop_t::generate_bulk_rounding_vector( cuopt_assert( test_var_out_of_bounds(orig_sol, unset_var_idx, second_probe, int_tol, sol.handle_ptr), "Variable out of original bounds!"); - // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( - // unset_var_idx, sol.handle_ptr->get_stream()) <= val_to_round + int_tol && - // val_to_round - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( - // unset_var_idx, sol.handle_ptr->get_stream()), - // "Variable out of original bounds!"); - // cuopt_assert(orig_sol.problem_ptr->variable_lower_bounds.element( - // unset_var_idx, sol.handle_ptr->get_stream()) <= second_probe + int_tol && - // second_probe - int_tol <= orig_sol.problem_ptr->variable_upper_bounds.element( - // unset_var_idx, sol.handle_ptr->get_stream()), - // "Variable out of original bounds!"); std::get<0>(var_probe_vals)[i] = unset_var_idx; std::get<1>(var_probe_vals)[i] = val_to_round; std::get<2>(var_probe_vals)[i] = second_probe; @@ -821,8 +769,6 @@ void constraint_prop_t::restore_original_bounds_on_unfixed( auto p_v_var_bnd = p_v.variable_bounds[var_idx]; if (!p_v.integer_equal(p_v_var_bnd.x, p_v_var_bnd.y) || !p_v.is_integer_var(var_idx)) { p_v.variable_bounds[var_idx] = op_v.variable_bounds[var_idx]; - // p_v.variable_lower_bounds[var_idx] = op_v.variable_lower_bounds[var_idx]; - // p_v.variable_upper_bounds[var_idx] = op_v.variable_upper_bounds[var_idx]; } }); } @@ -1169,10 +1115,6 @@ std::tuple constraint_prop_t::probing_values( "probing value out of bounds"); return std::make_tuple(first_round_val, var_val, second_round_val); } else { - // auto orig_v_lb = - // orig_sol.problem_ptr->variable_lower_bounds.element(idx, sol.handle_ptr->get_stream()); - // auto orig_v_ub = - // orig_sol.problem_ptr->variable_upper_bounds.element(idx, sol.handle_ptr->get_stream()); auto orig_v_bnd = orig_sol.problem_ptr->variable_bounds.element(idx, sol.handle_ptr->get_stream()); auto orig_v_lb = orig_v_bnd.x; diff --git a/cpp/src/mip/presolve/multi_probe.cu b/cpp/src/mip/presolve/multi_probe.cu index 58b59c19a3..8ff2fd871d 100644 --- a/cpp/src/mip/presolve/multi_probe.cu +++ b/cpp/src/mip/presolve/multi_probe.cu @@ -336,9 +336,7 @@ void multi_probe_t::update_host_bounds( const raft::device_span::type> variable_bounds) { cuopt_assert(variable_bounds.size() == host_lb.size(), "size of variable lower bound mismatch"); - // raft::copy(host_lb.data(), variable_lb.data(), variable_lb.size(), handle_ptr->get_stream()); cuopt_assert(variable_bounds.size() == host_ub.size(), "size of variable upper bound mismatch"); - // raft::copy(host_ub.data(), variable_ub.data(), variable_ub.size(), handle_ptr->get_stream()); rmm::device_uvector var_lb(variable_bounds.size(), handle_ptr->get_stream()); rmm::device_uvector var_ub(variable_bounds.size(), handle_ptr->get_stream()); diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 3728c9d40b..74dfd76007 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -126,10 +126,6 @@ problem_t::problem_t( objective_coefficients(problem_.get_objective_coefficients(), problem_.get_handle_ptr()->get_stream()), variable_bounds(0, problem_.get_handle_ptr()->get_stream()), - // variable_lower_bounds(problem_.get_variable_lower_bounds(), - // problem_.get_handle_ptr()->get_stream()), - // variable_upper_bounds(problem_.get_variable_upper_bounds(), - // problem_.get_handle_ptr()->get_stream()), constraint_lower_bounds(problem_.get_constraint_lower_bounds(), problem_.get_handle_ptr()->get_stream()), constraint_upper_bounds(problem_.get_constraint_upper_bounds(), @@ -179,8 +175,6 @@ problem_t::problem_t(const problem_t& problem_) offsets(problem_.offsets, handle_ptr->get_stream()), objective_coefficients(problem_.objective_coefficients, handle_ptr->get_stream()), variable_bounds(problem_.variable_bounds, handle_ptr->get_stream()), - // variable_lower_bounds(problem_.variable_lower_bounds, handle_ptr->get_stream()), - // variable_upper_bounds(problem_.variable_upper_bounds, handle_ptr->get_stream()), constraint_lower_bounds(problem_.constraint_lower_bounds, handle_ptr->get_stream()), constraint_upper_bounds(problem_.constraint_upper_bounds, handle_ptr->get_stream()), combined_bounds(problem_.combined_bounds, handle_ptr->get_stream()), @@ -256,16 +250,6 @@ problem_t::problem_t(const problem_t& problem_, bool no_deep (!no_deep_copy) ? rmm::device_uvector(problem_.variable_bounds, handle_ptr->get_stream()) : rmm::device_uvector(problem_.variable_bounds.size(), handle_ptr->get_stream())), - // variable_lower_bounds( - // (!no_deep_copy) - // ? rmm::device_uvector(problem_.variable_lower_bounds, handle_ptr->get_stream()) - // : rmm::device_uvector(problem_.variable_lower_bounds.size(), - // handle_ptr->get_stream())), - // variable_upper_bounds( - // (!no_deep_copy) - // ? rmm::device_uvector(problem_.variable_upper_bounds, handle_ptr->get_stream()) - // : rmm::device_uvector(problem_.variable_upper_bounds.size(), - // handle_ptr->get_stream())), constraint_lower_bounds( (!no_deep_copy) ? rmm::device_uvector(problem_.constraint_lower_bounds, handle_ptr->get_stream()) @@ -397,20 +381,12 @@ void problem_t::check_problem_representation(bool check_transposed, } // Check variable bounds are set and with the correct size - if (!empty) { - // cuopt_assert(!variable_lower_bounds.is_empty() && !variable_upper_bounds.is_empty(), - // "Variable lower bounds and variable upper bounds must be set."); - cuopt_assert(!variable_bounds.is_empty(), "Variable bounds must be set."); - } + if (!empty) { cuopt_assert(!variable_bounds.is_empty(), "Variable bounds must be set."); } cuopt_assert(variable_bounds.size() == objective_coefficients.size(), "Sizes for vectors related to the variables are not the same."); cuopt_assert(variable_bounds.size() == (std::size_t)n_variables, "Sizes for vectors related to the variables are not the same."); - // cuopt_assert(variable_lower_bounds.size() == objective_coefficients.size(), - // "Sizes for vectors related to the variables are not the same."); - // cuopt_assert(variable_upper_bounds.size() == objective_coefficients.size(), - // "Sizes for vectors related to the variables are not the same"); cuopt_assert(variable_types.size() == (std::size_t)n_variables, "Sizes for vectors related to the variables are not the same."); // Check constraints bounds sizes @@ -929,10 +905,6 @@ typename problem_t::view_t problem_t::view() v.objective_coefficients = raft::device_span{objective_coefficients.data(), objective_coefficients.size()}; v.variable_bounds = make_span(variable_bounds); - // v.variable_lower_bounds = - // raft::device_span{variable_lower_bounds.data(), variable_lower_bounds.size()}; - // v.variable_upper_bounds = - // raft::device_span{variable_upper_bounds.data(), variable_upper_bounds.size()}; v.constraint_lower_bounds = raft::device_span{constraint_lower_bounds.data(), constraint_lower_bounds.size()}; v.constraint_upper_bounds = @@ -956,8 +928,6 @@ template void problem_t::resize_variables(size_t size) { variable_bounds.resize(size, handle_ptr->get_stream()); - // variable_lower_bounds.resize(size, handle_ptr->get_stream()); - // variable_upper_bounds.resize(size, handle_ptr->get_stream()); variable_types.resize(size, handle_ptr->get_stream()); objective_coefficients.resize(size, handle_ptr->get_stream()); is_binary_variable.resize(size, handle_ptr->get_stream()); @@ -993,14 +963,6 @@ void problem_t::insert_variables(variables_delta_t& h_vars) h_vars.variable_bounds.data(), h_vars.variable_bounds.size(), handle_ptr->get_stream()); - // raft::copy(variable_lower_bounds.data() + n_variables, - // h_vars.lower_bounds.data(), - // h_vars.lower_bounds.size(), - // handle_ptr->get_stream()); - // raft::copy(variable_upper_bounds.data() + n_variables, - // h_vars.upper_bounds.data(), - // h_vars.upper_bounds.size(), - // handle_ptr->get_stream()); raft::copy(variable_types.data() + n_variables, h_vars.variable_types.data(), h_vars.variable_types.size(), @@ -1242,18 +1204,6 @@ void problem_t::remove_given_variables(problem_t& original_p original_problem.variable_bounds.begin(), variable_bounds.begin()); variable_bounds.resize(variable_map.size(), handle_ptr->get_stream()); - // thrust::gather(handle_ptr->get_thrust_policy(), - // variable_map.begin(), - // variable_map.end(), - // original_problem.variable_lower_bounds.begin(), - // variable_lower_bounds.begin()); - // variable_lower_bounds.resize(variable_map.size(), handle_ptr->get_stream()); - // thrust::gather(handle_ptr->get_thrust_policy(), - // variable_map.begin(), - // variable_map.end(), - // original_problem.variable_upper_bounds.begin(), - // variable_upper_bounds.begin()); - // variable_upper_bounds.resize(variable_map.size(), handle_ptr->get_stream()); thrust::gather(handle_ptr->get_thrust_policy(), variable_map.begin(), variable_map.end(), @@ -1370,10 +1320,8 @@ template void standardize_bounds(std::vector>>& variable_constraint_map, problem_t& pb) { - auto handle_ptr = pb.handle_ptr; - auto h_var_bounds = cuopt::host_copy(pb.variable_bounds); - // auto h_var_lower_bounds = cuopt::host_copy(pb.variable_lower_bounds); - // auto h_var_upper_bounds = cuopt::host_copy(pb.variable_upper_bounds); + auto handle_ptr = pb.handle_ptr; + auto h_var_bounds = cuopt::host_copy(pb.variable_bounds); auto h_objective_coefficients = cuopt::host_copy(pb.objective_coefficients); auto h_variable_types = cuopt::host_copy(pb.variable_types); handle_ptr->sync_stream(); @@ -1420,8 +1368,6 @@ void standardize_bounds(std::vector>>& variable_ // resize the device vectors is sizes are smaller if (pb.variable_bounds.size() < h_var_bounds.size()) { - // pb.variable_lower_bounds.resize(h_var_lower_bounds.size(), handle_ptr->get_stream()); - // pb.variable_upper_bounds.resize(h_var_lower_bounds.size(), handle_ptr->get_stream()); pb.variable_bounds.resize(h_var_bounds.size(), handle_ptr->get_stream()); pb.objective_coefficients.resize(h_objective_coefficients.size(), handle_ptr->get_stream()); pb.variable_types.resize(h_variable_types.size(), handle_ptr->get_stream()); @@ -1429,14 +1375,6 @@ void standardize_bounds(std::vector>>& variable_ raft::copy( pb.variable_bounds.data(), h_var_bounds.data(), h_var_bounds.size(), handle_ptr->get_stream()); - // raft::copy(pb.variable_lower_bounds.data(), - // h_var_lower_bounds.data(), - // h_var_lower_bounds.size(), - // handle_ptr->get_stream()); - // raft::copy(pb.variable_upper_bounds.data(), - // h_var_upper_bounds.data(), - // h_var_upper_bounds.size(), - // handle_ptr->get_stream()); raft::copy(pb.objective_coefficients.data(), h_objective_coefficients.data(), h_objective_coefficients.size(), @@ -1572,8 +1510,6 @@ void problem_t::get_host_user_problem( } } user_problem.num_range_rows = user_problem.range_rows.size(); - // user_problem.lower = cuopt::host_copy(variable_lower_bounds); - // user_problem.upper = cuopt::host_copy(variable_upper_bounds); std::tie(user_problem.lower, user_problem.upper) = extract_host_bounds(variable_bounds, handle_ptr); user_problem.problem_name = original_problem_ptr->get_problem_name(); diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index 9c16c8b0be..d88df30218 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -242,8 +242,6 @@ class problem_t { rmm::device_uvector objective_coefficients; using f_t2 = typename type_2::type; rmm::device_uvector variable_bounds; - // rmm::device_uvector variable_lower_bounds; - // rmm::device_uvector variable_upper_bounds; rmm::device_uvector constraint_lower_bounds; rmm::device_uvector constraint_upper_bounds; /* biggest between cstr lower and upper */ From 77c94d8a8fd7281d9e179cd5fb60dfe487766e61 Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Thu, 4 Sep 2025 15:21:42 -0400 Subject: [PATCH 07/10] timer fix --- cpp/src/mip/diversity/diversity_manager.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index d25eb9405f..618b24bd30 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -160,9 +160,9 @@ std::vector> diversity_manager_t::generate_more_s population.run_solution_callbacks(sol); solutions.emplace_back(solution_t(sol)); if (total_time_to_generate.check_time_limit()) { return solutions; } - timer_t timer(std::min(ls_limit, timer.remaining_time())); + timer_t ls_timer(std::min(ls_limit, timer.remaining_time())); ls_config_t ls_config; - run_local_search(sol, population.weights, timer, ls_config); + run_local_search(sol, population.weights, ls_timer, ls_config); population.run_solution_callbacks(sol); solutions.emplace_back(std::move(sol)); if (total_time_to_generate.check_time_limit()) { return solutions; } From cacd56745706919e599b92b99561d85ca45f1094 Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Fri, 5 Sep 2025 13:56:52 -0400 Subject: [PATCH 08/10] pr review fixes --- .../restart_strategy/pdlp_restart_strategy.cu | 2 +- .../utilities/problem_checking.cu | 10 +-- cpp/src/linear_programming/utils.cuh | 25 +++---- .../recombiners/bound_prop_recombiner.cuh | 4 +- .../mip/feasibility_jump/feasibility_jump.cu | 11 +-- .../feasibility_jump_kernels.cu | 46 ++++++------ .../mip/feasibility_jump/load_balancing.cuh | 11 +-- .../feasibility_pump/feasibility_pump.cu | 19 ++--- .../local_search/rounding/bounds_repair.cu | 72 +++++++++---------- .../local_search/rounding/bounds_repair.cuh | 11 +-- .../local_search/rounding/constraint_prop.cu | 60 ++++++++-------- .../rounding/simple_rounding_kernels.cuh | 4 +- cpp/src/mip/presolve/bounds_presolve.cu | 11 +-- .../conditional_bound_strengthening.cu | 12 ++-- .../mip/presolve/lb_bounds_udpate_data.cuh | 69 ++++++++++++++++++ cpp/src/mip/presolve/lb_probing_cache.cu | 6 +- cpp/src/mip/presolve/multi_probe.cu | 26 ++++--- cpp/src/mip/presolve/probing_cache.cu | 16 ++--- .../mip/presolve/trivial_presolve_helpers.cuh | 15 ++-- cpp/src/mip/problem/problem.cu | 56 +++++++-------- cpp/src/mip/problem/problem.cuh | 19 +++-- cpp/src/mip/problem/problem_helpers.cuh | 28 +------- cpp/src/mip/solution/feasibility_test.cuh | 8 +-- cpp/src/mip/solution/solution.cu | 16 ++--- cpp/src/mip/solve.cu | 3 +- cpp/src/mip/utils.cuh | 8 +-- cpp/src/utilities/copy_helpers.hpp | 42 +++++++++-- 27 files changed, 349 insertions(+), 261 deletions(-) create mode 100644 cpp/src/mip/presolve/lb_bounds_udpate_data.cuh diff --git a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu index 759179066b..cf23c8a1d7 100644 --- a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu +++ b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu @@ -1392,7 +1392,7 @@ template struct extract_bounds_t { __device__ thrust::tuple operator()(f_t2 bounds) { - return thrust::make_tuple(bounds.x, bounds.y); + return thrust::make_tuple(get_lower(bounds), get_upper(bounds)); } }; diff --git a/cpp/src/linear_programming/utilities/problem_checking.cu b/cpp/src/linear_programming/utilities/problem_checking.cu index 702741cc21..b53021a959 100644 --- a/cpp/src/linear_programming/utilities/problem_checking.cu +++ b/cpp/src/linear_programming/utilities/problem_checking.cu @@ -290,8 +290,10 @@ void problem_checking_t::check_scaled_problem( auto var_type = variable_types[i]; if (var_type == var_t::INTEGER) { // Integers should be untouched - cuopt_assert(variable_bounds[i].y == scaled_variable_bounds[i].y, "Mismatch upper scaling"); - cuopt_assert(variable_bounds[i].x == scaled_variable_bounds[i].x, "Mismatch lower scaling"); + cuopt_assert(get_lower(variable_bounds[i]) == get_lower(scaled_variable_bounds[i]), + "Mismatch lower scaling"); + cuopt_assert(get_upper(variable_bounds[i]) == get_upper(scaled_variable_bounds[i]), + "Mismatch upper scaling"); } } } @@ -313,8 +315,8 @@ void problem_checking_t::check_unscaled_solution( h_assignment.data(), assignment.data(), assignment.size(), op_problem.handle_ptr->get_stream()); const f_t int_tol = op_problem.tolerances.integrality_tolerance; for (size_t i = 0; i < variable_bounds.size(); ++i) { - cuopt_assert(h_assignment[i] <= variable_bounds[i].y + int_tol, "Excess upper bound"); - cuopt_assert(h_assignment[i] >= variable_bounds[i].x - int_tol, "Excess lower bound"); + cuopt_assert(h_assignment[i] >= get_lower(variable_bounds[i]) - int_tol, "Excess lower bound"); + cuopt_assert(h_assignment[i] <= get_upper(variable_bounds[i]) + int_tol, "Excess upper bound"); } } diff --git a/cpp/src/linear_programming/utils.cuh b/cpp/src/linear_programming/utils.cuh index 34085b7df4..de107a66fb 100644 --- a/cpp/src/linear_programming/utils.cuh +++ b/cpp/src/linear_programming/utils.cuh @@ -86,8 +86,8 @@ struct primal_projection { f_t AtY, f_t2 bounds) { - f_t lower = bounds.x; - f_t upper = bounds.y; + f_t lower = get_lower(bounds); + f_t upper = get_upper(bounds); f_t gradient = obj_coeff - AtY; f_t next = primal - (*step_size_ * gradient); next = raft::max(raft::min(next, upper), lower); @@ -144,7 +144,7 @@ template struct clamp { __device__ f_t operator()(f_t value, f_t2 bounds) { - return raft::min(raft::max(value, bounds.x), bounds.y); + return raft::min(raft::max(value, get_lower(bounds)), get_upper(bounds)); } }; @@ -196,8 +196,8 @@ struct max_violation { { const f_t value = thrust::get<0>(t); const f_t2 bounds = thrust::get<1>(t); - const f_t lower = bounds.x; - const f_t upper = bounds.y; + const f_t lower = get_lower(bounds); + const f_t upper = get_upper(bounds); f_t local_max = f_t(0.0); if (isfinite(lower)) { local_max = raft::max(local_max, -value); } if (isfinite(upper)) { local_max = raft::max(local_max, value); } @@ -210,13 +210,10 @@ struct divide_check_zero { __device__ f_t2 operator()(f_t2 bounds, f_t value) { if (value == f_t{0}) { - bounds.x = f_t{0}; - bounds.y = f_t{0}; + return f_t2{0, 0}; } else { - bounds.x = bounds.x / value; - bounds.y = bounds.y / value; + return f_t2{get_lower(bounds) / value, get_upper(bounds) / value}; } - return bounds; } }; @@ -224,8 +221,8 @@ template struct bound_value_gradient { __device__ f_t operator()(f_t value, f_t2 bounds) { - f_t lower = bounds.x; - f_t upper = bounds.y; + f_t lower = get_lower(bounds); + f_t upper = get_upper(bounds); if (value > f_t(0) && value < f_t(0)) { return 0; } return value > f_t(0) ? lower : upper; } @@ -252,8 +249,8 @@ template struct bound_value_reduced_cost_product { __device__ f_t operator()(f_t value, f_t2 variable_bounds) { - f_t lower = variable_bounds.x; - f_t upper = variable_bounds.y; + f_t lower = get_lower(variable_bounds); + f_t upper = get_upper(variable_bounds); f_t bound_value = f_t(0); if (value > f_t(0)) { // A positive reduced cost is associated with a binding lower bound. diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index d9133f2993..52347fc8b7 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -87,8 +87,8 @@ class bound_prop_recombiner_t : public recombiner_t { probing_values[idx] = thrust::make_pair(other_val, second_val); // assign some floating value, so that they can be rounded by bounds prop auto bounds = guiding_view.problem.variable_bounds[idx]; - f_t lb = bounds.x; - f_t ub = bounds.y; + f_t lb = get_lower(bounds); + f_t ub = get_upper(bounds); if (integer_equal(lb, ub, int_tol)) { cuopt_assert(false, "The var values must be different in A and B!"); } else if (isfinite(lb)) { diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index b9e0d52d0b..7520d1431e 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -291,9 +291,9 @@ void fj_t::device_init(const rmm::cuda_stream_view& stream) cuopt_assert(var_idx < pb.is_binary_variable.size(), ""); if (pb.is_binary_variable[var_idx]) { - cuopt_assert( - pb.variable_bounds[var_idx].x == 0 && pb.variable_bounds[var_idx].y == 1, - "invalid bounds for binary variable"); + cuopt_assert(get_lower(pb.variable_bounds[var_idx]) == 0 && + get_upper(pb.variable_bounds[var_idx]) == 1, + "invalid bounds for binary variable"); } }); } @@ -392,8 +392,9 @@ void fj_t::climber_init(i_t climber_idx, const rmm::cuda_stream_view& incumbent_assignment[var_idx] = round(incumbent_assignment[var_idx]); } // clamp to bounds - auto bounds = pb.variable_bounds[var_idx]; - incumbent_assignment[var_idx] = max(bounds.x, min(bounds.y, incumbent_assignment[var_idx])); + auto bounds = pb.variable_bounds[var_idx]; + incumbent_assignment[var_idx] = + max(get_lower(bounds), min(get_upper(bounds), incumbent_assignment[var_idx])); }); thrust::for_each( diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 575144ee7f..0d96cd4b2a 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -439,14 +439,14 @@ DI std::pair::move_score_info_t> compute_best_mtm( // fixed variables auto bounds = fj.pb.variable_bounds[var_idx]; - if (fj.pb.integer_equal(bounds.x, bounds.y)) { - return std::make_pair(bounds.x, fj_t::move_score_info_t::invalid()); + if (fj.pb.integer_equal(get_lower(bounds), get_upper(bounds))) { + return std::make_pair(get_lower(bounds), fj_t::move_score_info_t::invalid()); } f_t old_val = fj.incumbent_assignment[var_idx]; f_t obj_coeff = fj.pb.objective_coefficients[var_idx]; - f_t v_lb = bounds.x; - f_t v_ub = bounds.y; + f_t v_lb = get_lower(bounds); + f_t v_ub = get_upper(bounds); raft::random::PCGenerator rng(fj.settings->seed + *fj.iterations, 0, 0); cuopt_assert(isfinite(v_lb) || isfinite(v_ub), "unexpected free variable"); @@ -548,7 +548,8 @@ DI void update_jump_value(typename fj_t::climber_data_t::view_t fj, i_ cuopt_assert(fj.pb.integer_equal(fj.incumbent_assignment[var_idx], 0) || fj.pb.integer_equal(fj.incumbent_assignment[var_idx], 1), "Current assignment is not binary!"); - cuopt_assert(fj.pb.variable_bounds[var_idx].x == 0 && fj.pb.variable_bounds[var_idx].y == 1, + cuopt_assert(get_lower(fj.pb.variable_bounds[var_idx]) == 0 && + get_upper(fj.pb.variable_bounds[var_idx]) == 1, ""); cuopt_assert( fj.pb.check_variable_within_bounds(var_idx, fj.incumbent_assignment[var_idx] + delta), @@ -564,7 +565,8 @@ DI void update_jump_value(typename fj_t::climber_data_t::view_t fj, i_ } else { delta = round(1.0 - 2 * fj.incumbent_assignment[var_idx]); if (threadIdx.x == 0) { - cuopt_assert(fj.pb.variable_bounds[var_idx].x == 0 && fj.pb.variable_bounds[var_idx].y == 1, + cuopt_assert(get_lower(fj.pb.variable_bounds[var_idx]) == 0 && + get_upper(fj.pb.variable_bounds[var_idx]) == 1, ""); cuopt_assert( fj.pb.check_variable_within_bounds(var_idx, fj.incumbent_assignment[var_idx] + delta), @@ -794,7 +796,7 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t } auto bounds = fj.pb.variable_bounds[var_idx]; - i_t var_range = bounds.y - bounds.x; + i_t var_range = get_upper(bounds) - get_lower(bounds); double delta_rel_err = fabs(fj.jump_move_delta[var_idx]) / var_range; if (delta_rel_err < fj.settings->parameters.small_move_tabu_threshold) { *fj.small_move_tabu = *fj.iterations; @@ -806,8 +808,8 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t "err_range %.2g%%, infeas %.2g, total viol %d\n", *fj.iterations, var_idx, - fj.pb.variable_bounds[var_idx].x, - fj.pb.variable_bounds[var_idx].y, + get_lower(fj.pb.variable_bounds[var_idx]), + get_upper(fj.pb.variable_bounds[var_idx]), fj.incumbent_assignment[var_idx], fj.jump_move_delta[var_idx], fj.incumbent_assignment[var_idx] + fj.jump_move_delta[var_idx], @@ -891,8 +893,8 @@ DI void update_lift_moves(typename fj_t::climber_data_t::view_t fj) f_t delta = -std::numeric_limits::infinity(); auto bounds = fj.pb.variable_bounds[var_idx]; - f_t th_lower_delta = bounds.x - fj.incumbent_assignment[var_idx]; - f_t th_upper_delta = bounds.y - fj.incumbent_assignment[var_idx]; + f_t th_lower_delta = get_lower(bounds) - fj.incumbent_assignment[var_idx]; + f_t th_upper_delta = get_upper(bounds) - fj.incumbent_assignment[var_idx]; auto [offset_begin, offset_end] = fj.pb.reverse_range_for_var(var_idx); for (i_t j = threadIdx.x + offset_begin; j < offset_end; j += blockDim.x) { auto cstr_idx = fj.pb.reverse_constraints[j]; @@ -993,8 +995,8 @@ DI f_t get_breakthrough_move(typename fj_t::climber_data_t::view_t fj, { f_t obj_coeff = fj.pb.objective_coefficients[var_idx]; auto bounds = fj.pb.variable_bounds[var_idx]; - f_t v_lb = bounds.x; - f_t v_ub = bounds.y; + f_t v_lb = get_lower(bounds); + f_t v_ub = get_upper(bounds); cuopt_assert(isfinite(v_lb) || isfinite(v_ub), "unexpected free variable"); cuopt_assert(v_lb <= v_ub, "invalid bounds"); cuopt_assert(fj.pb.check_variable_within_bounds(var_idx, fj.incumbent_assignment[var_idx]), @@ -1219,7 +1221,7 @@ __device__ void compute_mtm_moves(typename fj_t::climber_data_t::view_ bool exclude_from_search = false; // "fixed" variables are to be excluded (as they cannot take any other value) auto bounds = fj.pb.variable_bounds[var_idx]; - exclude_from_search |= fj.pb.integer_equal(bounds.x, bounds.y); + exclude_from_search |= fj.pb.integer_equal(get_lower(bounds), get_upper(bounds)); if (exclude_from_search) { if (threadIdx.x == 0) { @@ -1274,7 +1276,7 @@ __global__ void select_variable_kernel(typename fj_t::climber_data_t:: auto move_score = fj.jump_move_scores[var_idx]; auto bounds = fj.pb.variable_bounds[var_idx]; - i_t var_range = bounds.y - bounds.x; + i_t var_range = get_upper(bounds) - get_lower(bounds); double delta_rel_err = fabs(fj.jump_move_delta[var_idx]) / var_range; // tabu for small moves to avoid very long descents/numerical issues if (delta_rel_err < fj.settings->parameters.small_move_tabu_threshold && @@ -1322,15 +1324,15 @@ __global__ void select_variable_kernel(typename fj_t::climber_data_t:: if (selected_var != std::numeric_limits::max()) { #if FJ_SINGLE_STEP auto bounds = fj.pb.variable_bounds[selected_var]; - i_t var_range = bounds.y - bounds.x; + i_t var_range = get_upper(bounds) - get_lower(bounds); double delta_rel_err = fabs(fj.jump_move_delta[selected_var]) / var_range * 100; DEVICE_LOG_INFO( "=---- FJ: selected %d [%g/%g] %c :%.4g+{%.4g}=%.4g score {%g,%g}, d_obj %.2g+%.2g->%.2g, " "delta_rel_err %.2g%%, " "infeas %.2g, total viol %d, out of %d\n", selected_var, - bounds.x, - bounds.y, + get_lower(bounds), + get_upper(bounds), fj.pb.variable_types[selected_var] == var_t::INTEGER ? 'I' : 'C', fj.incumbent_assignment[selected_var], fj.jump_move_delta[selected_var], @@ -1557,8 +1559,8 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat if (fj.pb.integer_equal(delta, 0)) { auto bounds = fj.pb.variable_bounds[selected]; delta = round_nearest(fj.incumbent_assignment[selected], - bounds.x, - bounds.y, + get_lower(bounds), + get_upper(bounds), fj.pb.tolerances.integrality_tolerance, rng) - fj.incumbent_assignment[selected]; @@ -1570,8 +1572,8 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat auto bounds = fj.pb.variable_bounds[*fj.selected_var]; DEVICE_LOG_TRACE("selected_var: %d bounds [%.4g/%.4g], delta %g, old val %g\n", *fj.selected_var, - bounds.x, - bounds.y, + get_lower(bounds), + get_upper(bounds), fj.jump_move_delta[*fj.selected_var], fj.incumbent_assignment[*fj.selected_var]); } diff --git a/cpp/src/mip/feasibility_jump/load_balancing.cuh b/cpp/src/mip/feasibility_jump/load_balancing.cuh index 7922d6e5f6..67af9c06ae 100644 --- a/cpp/src/mip/feasibility_jump/load_balancing.cuh +++ b/cpp/src/mip/feasibility_jump/load_balancing.cuh @@ -322,7 +322,8 @@ __global__ void load_balancing_compute_scores_binary( if (threadIdx.x == 0) { cuopt_assert(fj.incumbent_assignment[var_idx] == 0 || fj.incumbent_assignment[var_idx] == 1, "Current assignment is not binary!"); - cuopt_assert(fj.pb.variable_bounds[var_idx].x == 0 && fj.pb.variable_bounds[var_idx].y == 1, + cuopt_assert(get_lower(fj.pb.variable_bounds[var_idx]) == 0 && + get_upper(fj.pb.variable_bounds[var_idx]) == 1, ""); cuopt_assert( fj.pb.check_variable_within_bounds(var_idx, fj.incumbent_assignment[var_idx] + delta), @@ -401,8 +402,8 @@ __global__ void load_balancing_mtm_compute_candidates( f_t c_lb = fj.constraint_lower_bounds_csr[csr_offset]; f_t c_ub = fj.constraint_upper_bounds_csr[csr_offset]; auto v_bnd = fj.pb.variable_bounds[var_idx]; - f_t v_lb = v_bnd.x; - f_t v_ub = v_bnd.y; + f_t v_lb = get_lower(v_bnd); + f_t v_ub = get_upper(v_bnd); cuopt_assert(c_lb == fj.pb.constraint_lower_bounds[cstr_idx], ""); cuopt_assert(c_ub == fj.pb.constraint_upper_bounds[cstr_idx], ""); @@ -514,8 +515,8 @@ __launch_bounds__(TPB_loadbalance, 16) __global__ } auto v_bnd = fj.pb.variable_bounds[var_idx]; - f_t v_lb = v_bnd.x; - f_t v_ub = v_bnd.y; + f_t v_lb = get_lower(v_bnd); + f_t v_ub = get_upper(v_bnd); // candidate counts is usually very small (<4) thanks to early duplicate deletion in the // previous kernel rarely limits the thoroughput nor leads to noticeable imbalance diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index 40edcdc1cc..f8b7d34fc5 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -161,20 +161,23 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tinteger_equal(h_assignment[i], h_var_bounds.y)) { - obj_offset += h_var_bounds.y; + if (solution.problem_ptr->integer_equal(h_assignment[i], get_upper(h_var_bounds))) { + obj_offset += get_upper(h_var_bounds); // set the objective weight to -1, u - x obj_coefficients[i] = -1; - } else if (solution.problem_ptr->integer_equal(h_assignment[i], h_var_bounds.x)) { - obj_offset -= h_var_bounds.x; + } else if (solution.problem_ptr->integer_equal(h_assignment[i], get_lower(h_var_bounds))) { + obj_offset -= get_lower(h_var_bounds); // set the objective weight to +1, x - l obj_coefficients[i] = 1; } else { // objective weight is 1 const f_t obj_weight = 1.; // the distance should always be positive - i_t var_id = h_variables.add_variable( - 0, (h_var_bounds.y - h_var_bounds.x) + int_tol, obj_weight, var_t::CONTINUOUS); + i_t var_id = + h_variables.add_variable(0, + (get_upper(h_var_bounds) - get_lower(h_var_bounds)) + int_tol, + obj_weight, + var_t::CONTINUOUS); obj_coefficients.push_back(obj_weight); f_t dist_val = abs(h_assignment[i] - h_last_projection[i]); // if it is out of bounds, because of the approximation issues,or init issues @@ -450,8 +453,8 @@ void feasibility_pump_t::relax_general_integers(solution_t& [var_types, var_bnds, copy_types, pb = solution.problem_ptr->view()] __device__(auto v_idx) { auto orig_v_type = var_types[v_idx]; auto var_bounds = var_bnds[v_idx]; - auto lb = var_bounds.x; - auto ub = var_bounds.y; + auto lb = get_lower(var_bounds); + auto ub = get_upper(var_bounds); bool var_binary = (pb.integer_equal(lb, 0) && pb.integer_equal(ub, 1)); auto copy_type = (orig_v_type == var_t::INTEGER) && var_binary ? var_t::INTEGER : var_t::CONTINUOUS; diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index b4c53579e6..e77702c38f 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -154,10 +154,10 @@ i_t bounds_repair_t::compute_best_shift(problem_t& problem, if (shift_amount != 0.) { auto var_bnd = pb_v.variable_bounds[var_idx]; auto o_var_bnd = o_pb_v.variable_bounds[var_idx]; - f_t var_lb = var_bnd.x; - f_t var_ub = var_bnd.y; - f_t o_var_lb = o_var_bnd.x; - f_t o_var_ub = o_var_bnd.y; + f_t var_lb = get_lower(var_bnd); + f_t var_ub = get_upper(var_bnd); + f_t o_var_lb = get_lower(o_var_bnd); + f_t o_var_ub = get_upper(o_var_bnd); cuopt_assert(var_lb + pb_v.tolerances.integrality_tolerance >= o_var_lb, ""); cuopt_assert(o_var_ub + pb_v.tolerances.integrality_tolerance >= var_ub, ""); // round the shift amount of integer @@ -214,8 +214,8 @@ __global__ void compute_damages_kernel(typename problem_t::view_t prob i_t var_idx = candidates.variable_index[blockIdx.x]; f_t shift_amount = candidates.bound_shift[blockIdx.x]; auto v_bnd = problem.variable_bounds[var_idx]; - f_t v_lb = v_bnd.x; - f_t v_ub = v_bnd.y; + f_t v_lb = get_lower(v_bnd); + f_t v_ub = get_upper(v_bnd); f_t th_damage = 0.; i_t n_infeasible_cstr_delta = 0; auto [offset_begin, offset_end] = problem.reverse_range_for_var(var_idx); @@ -351,37 +351,37 @@ void bounds_repair_t::apply_move(problem_t& problem, problem_t& original_problem, i_t move_idx) { - run_device_lambda( - handle_ptr->get_stream(), - [move_idx, - candidates = candidates.view(), - problem = problem.view(), - original_problem = original_problem.view()] __device__() { - i_t var_idx = candidates.variable_index[move_idx]; - f_t shift_value = candidates.bound_shift[move_idx]; - auto bounds = problem.variable_bounds[var_idx]; - DEVICE_LOG_TRACE("Applying move on var %d with shift %f lb %f ub %f o_lb %f o_ub %f \n", - var_idx, - shift_value, - bounds.x, - bounds.y, - original_problem.variable_bounds[var_idx].x, - original_problem.variable_bounds[var_idx].y); - if (problem.integer_equal(bounds.x, bounds.y)) { - *candidates.at_least_one_singleton_moved = 1; - } + run_device_lambda(handle_ptr->get_stream(), + [move_idx, + candidates = candidates.view(), + problem = problem.view(), + original_problem = original_problem.view()] __device__() { + i_t var_idx = candidates.variable_index[move_idx]; + f_t shift_value = candidates.bound_shift[move_idx]; + auto bounds = problem.variable_bounds[var_idx]; + DEVICE_LOG_TRACE( + "Applying move on var %d with shift %f lb %f ub %f o_lb %f o_ub %f \n", + var_idx, + shift_value, + get_lower(bounds), + get_upper(bounds), + get_lower(original_problem.variable_bounds[var_idx]), + get_upper(original_problem.variable_bounds[var_idx])); + if (problem.integer_equal(get_lower(bounds), get_upper(bounds))) { + *candidates.at_least_one_singleton_moved = 1; + } - bounds.x += shift_value; - bounds.y += shift_value; - problem.variable_bounds[var_idx] = bounds; - cuopt_assert(original_problem.variable_bounds[var_idx].x <= - bounds.y + problem.tolerances.integrality_tolerance, - ""); - cuopt_assert( - original_problem.variable_bounds[var_idx].y + problem.tolerances.integrality_tolerance >= - bounds.y, - ""); - }); + get_lower(bounds) += shift_value; + get_upper(bounds) += shift_value; + problem.variable_bounds[var_idx] = bounds; + cuopt_assert(get_lower(original_problem.variable_bounds[var_idx]) <= + get_lower(bounds) + problem.tolerances.integrality_tolerance, + ""); + cuopt_assert(get_upper(original_problem.variable_bounds[var_idx]) + + problem.tolerances.integrality_tolerance >= + get_upper(bounds), + ""); + }); } template diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cuh b/cpp/src/mip/local_search/rounding/bounds_repair.cuh index dec907c761..8cc392c0b9 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cuh +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cuh @@ -45,11 +45,12 @@ struct bounds_t { { cuopt_assert(lb.size() == pb.variable_bounds.size(), ""); cuopt_assert(ub.size() == pb.variable_bounds.size(), ""); - thrust::transform(handle_ptr->get_thrust_policy(), - pb.variable_bounds.begin(), - pb.variable_bounds.end(), - thrust::make_zip_iterator(thrust::make_tuple(lb.begin(), ub.begin())), - [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); + thrust::transform( + handle_ptr->get_thrust_policy(), + pb.variable_bounds.begin(), + pb.variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(lb.begin(), ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(get_lower(i), get_upper(i)); }); }; void update_to(problem_t& pb, const raft::handle_t* handle_ptr) { diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 06f1e745af..e250cb7554 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -131,8 +131,8 @@ __global__ void compute_implied_slack_consumption_per_var( i_t var_degree = pb.reverse_offsets[var_idx + 1] - var_offset; f_t th_var_implied_slack_consumption = 0.; auto var_bnd = pb.variable_bounds[var_idx]; - f_t lb = var_bnd.x; - f_t ub = var_bnd.y; + f_t lb = get_lower(var_bnd); + f_t ub = get_upper(var_bnd); for (i_t i = threadIdx.x; i < var_degree; i += blockDim.x) { auto a = pb.reverse_coefficients[var_offset + i]; auto cnst_idx = pb.reverse_constraints[var_offset + i]; @@ -214,8 +214,8 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t [bnds = sol.problem_ptr->variable_bounds.data(), assgn] __device__(i_t v_idx_1, i_t v_idx_2) { auto bnd_1 = bnds[v_idx_1]; auto bnd_2 = bnds[v_idx_2]; - f_t bounds_interval_1 = bnd_1.y - bnd_1.x; - f_t bounds_interval_2 = bnd_2.y - bnd_2.x; + f_t bounds_interval_1 = get_upper(bnd_1) - get_lower(bnd_1); + f_t bounds_interval_2 = get_upper(bnd_2) - get_lower(bnd_2); // if bounds interval are equal (binary and ternary) check fraction // if both bounds intervals are greater than 2. then do fraction if ((bounds_interval_1 == bounds_interval_2) || @@ -247,8 +247,8 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t i_t var_2 = vars[idx + 1]; auto bnd_1 = bnds[var_1]; auto bnd_2 = bnds[var_2]; - f_t bounds_interval_1 = bnd_1.y - bnd_1.x; - f_t bounds_interval_2 = bnd_2.y - bnd_2.x; + f_t bounds_interval_1 = get_upper(bnd_1) - get_lower(bnd_1); + f_t bounds_interval_2 = get_upper(bnd_2) - get_lower(bnd_2); f_t frac_1 = get_fractionality_of_val(assgn[var_1]); f_t frac_2 = get_fractionality_of_val(assgn[var_2]); if (bounds_interval_1 == 1 && bounds_interval_2 == 1) { @@ -405,10 +405,10 @@ void constraint_prop_t::collapse_crossing_bounds(problem_t& int_tol = problem.tolerances.integrality_tolerance] __device__(i_t idx) { auto v_bnd = v_bnds[idx]; auto ov_bnd = original_v_bnds[idx]; - auto v_lb = v_bnd.x; - auto v_ub = v_bnd.y; - auto o_lb = ov_bnd.x; - auto o_ub = ov_bnd.y; + auto v_lb = get_lower(v_bnd); + auto v_ub = get_upper(v_bnd); + auto o_lb = get_lower(ov_bnd); + auto o_ub = get_upper(ov_bnd); if (v_lb > v_ub) { f_t val_to_collapse; if (variable_types[idx] == var_t::INTEGER) { @@ -462,11 +462,11 @@ struct is_bound_fixed_t { HDI bool operator()(i_t idx) { auto v_bnd = bnd[idx]; - auto v_lb = v_bnd.x; - auto v_ub = v_bnd.y; + auto v_lb = get_lower(v_bnd); + auto v_ub = get_upper(v_bnd); auto ov_bnd = original_bnd[idx]; - auto o_lb = ov_bnd.x; - auto o_ub = ov_bnd.y; + auto o_lb = get_lower(ov_bnd); + auto o_ub = get_upper(ov_bnd); bool is_singleton = round_val_on_singleton_and_crossing(assignment[idx], v_lb, v_ub, o_lb, o_ub); return is_singleton; @@ -538,7 +538,9 @@ void constraint_prop_t::copy_bounds( input_bounds.begin(), input_bounds.end(), thrust::make_zip_iterator(thrust::make_tuple(output_lb.begin(), output_ub.begin())), - [] __device__(auto bounds) { return thrust::make_tuple(bounds.x, bounds.y); }); + [] __device__(auto bounds) { + return thrust::make_tuple(get_lower(bounds), get_upper(bounds)); + }); } template @@ -664,7 +666,7 @@ bool test_var_out_of_bounds(const solution_t& orig_sol, { auto var_bnd = orig_sol.problem_ptr->variable_bounds.element(unset_var_idx, handle_ptr->get_stream()); - return (var_bnd.x <= probe + int_tol) && (probe - int_tol <= var_bnd.y); + return (get_lower(var_bnd) <= probe + int_tol) && (probe - int_tol <= get_upper(var_bnd)); } template @@ -743,7 +745,7 @@ template struct extract_bounds_t { __device__ thrust::tuple operator()(f_t2 bounds) { - return thrust::make_tuple(bounds.x, bounds.y); + return thrust::make_tuple(get_lower(bounds), get_upper(bounds)); } }; @@ -760,16 +762,16 @@ void constraint_prop_t::restore_original_bounds_on_unfixed( problem_t& original_problem, const raft::handle_t* handle_ptr) { - thrust::for_each( - handle_ptr->get_thrust_policy(), - thrust::make_counting_iterator(0), - thrust::make_counting_iterator(problem.n_variables), - [p_v = problem.view(), op_v = original_problem.view()] __device__(i_t var_idx) { - auto p_v_var_bnd = p_v.variable_bounds[var_idx]; - if (!p_v.integer_equal(p_v_var_bnd.x, p_v_var_bnd.y) || !p_v.is_integer_var(var_idx)) { - p_v.variable_bounds[var_idx] = op_v.variable_bounds[var_idx]; - } - }); + thrust::for_each(handle_ptr->get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(problem.n_variables), + [p_v = problem.view(), op_v = original_problem.view()] __device__(i_t var_idx) { + auto p_v_var_bnd = p_v.variable_bounds[var_idx]; + if (!p_v.integer_equal(get_lower(p_v_var_bnd), get_upper(p_v_var_bnd)) || + !p_v.is_integer_var(var_idx)) { + p_v.variable_bounds[var_idx] = op_v.variable_bounds[var_idx]; + } + }); } template @@ -1116,8 +1118,8 @@ std::tuple constraint_prop_t::probing_values( } else { auto orig_v_bnd = orig_sol.problem_ptr->variable_bounds.element(idx, sol.handle_ptr->get_stream()); - auto orig_v_lb = orig_v_bnd.x; - auto orig_v_ub = orig_v_bnd.y; + auto orig_v_lb = get_lower(orig_v_bnd); + auto orig_v_ub = get_upper(orig_v_bnd); cuopt_assert(v_lb >= orig_v_lb, "Current lb should be greater than original lb"); cuopt_assert(v_ub <= orig_v_ub, "Current ub should be smaller than original ub"); v_lb = std::max(v_lb, orig_v_lb); diff --git a/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh b/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh index 29edf3e16d..1c0103110c 100644 --- a/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh +++ b/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh @@ -37,8 +37,8 @@ __global__ void nearest_rounding_kernel(typename solution_t::view_t so if (solution.problem.is_integer(curr_val)) { return; } const f_t int_tol = solution.problem.tolerances.integrality_tolerance; auto var_bnd = solution.problem.variable_bounds[var_id]; - f_t lb = var_bnd.x; - f_t ub = var_bnd.y; + f_t lb = get_lower(var_bnd); + f_t ub = get_upper(var_bnd); f_t nearest_val = round_nearest(curr_val, lb, ub, int_tol, rng); solution.assignment[var_id] = nearest_val; } diff --git a/cpp/src/mip/presolve/bounds_presolve.cu b/cpp/src/mip/presolve/bounds_presolve.cu index 800abd2d12..35d2ee0821 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cu +++ b/cpp/src/mip/presolve/bounds_presolve.cu @@ -224,11 +224,12 @@ void bound_presolve_t::copy_input_bounds(problem_t& pb) cuopt_assert(upd.lb.size() == pb.variable_bounds.size(), "size of variable lower bound mismatch"); cuopt_assert(upd.ub.size() == pb.variable_bounds.size(), "size of variable upper bound mismatch"); - thrust::transform(handle_ptr->get_thrust_policy(), - pb.variable_bounds.begin(), - pb.variable_bounds.end(), - thrust::make_zip_iterator(thrust::make_tuple(upd.lb.begin(), upd.ub.begin())), - [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); + thrust::transform( + handle_ptr->get_thrust_policy(), + pb.variable_bounds.begin(), + pb.variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(upd.lb.begin(), upd.ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(get_lower(i), get_upper(i)); }); } template diff --git a/cpp/src/mip/presolve/conditional_bound_strengthening.cu b/cpp/src/mip/presolve/conditional_bound_strengthening.cu index bf04088d54..4eb8417b73 100644 --- a/cpp/src/mip/presolve/conditional_bound_strengthening.cu +++ b/cpp/src/mip/presolve/conditional_bound_strengthening.cu @@ -497,10 +497,10 @@ __global__ void update_constraint_bounds_kernel(typename problem_t::vi raft::device_span lock_per_constraint) { auto constraint_pair = constraint_pairs[blockIdx.x]; - int constr_i = constraint_pair.x; + int constr_i = get_lower(constraint_pair); if (constr_i == -1) { return; } - int constr_j = constraint_pair.y; + int constr_j = get_upper(constraint_pair); // FIXME:: for now handle only the constraints that fit in shared i_t offset_j = pb.offsets[constr_j]; @@ -551,8 +551,8 @@ __global__ void update_constraint_bounds_kernel(typename problem_t::vi i_t variable_j = pb.variables[offset_j + tid]; a[tid] = pb.coefficients[offset_j + tid]; auto bounds = pb.variable_bounds[variable_j]; - lb[tid] = bounds.x; - ub[tid] = bounds.y; + lb[tid] = get_lower(bounds); + ub[tid] = get_upper(bounds); vtypes[tid] = pb.variable_types[variable_j]; c[tid] = 0.; @@ -577,8 +577,8 @@ __global__ void update_constraint_bounds_kernel(typename problem_t::vi f_t coeff = pb.coefficients[offset_i + index]; auto bounds = pb.variable_bounds[variable_i]; - f_t li = bounds.x; - f_t ui = bounds.y; + f_t li = get_lower(bounds); + f_t ui = get_upper(bounds); min_activity_if_not_participating += (coeff > 0. ? coeff * li : coeff * ui); max_activity_if_not_participating += (coeff > 0. ? coeff * ui : coeff * li); } diff --git a/cpp/src/mip/presolve/lb_bounds_udpate_data.cuh b/cpp/src/mip/presolve/lb_bounds_udpate_data.cuh new file mode 100644 index 0000000000..ad667aac11 --- /dev/null +++ b/cpp/src/mip/presolve/lb_bounds_udpate_data.cuh @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights + * reserved. SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +namespace cuopt::linear_programming::detail { + +template +struct lb_bounds_update_data_t { + rmm::device_scalar bounds_changed; + rmm::device_uvector cnst_slack; + rmm::device_uvector vars_bnd; + rmm::device_uvector tmp_act; + rmm::device_uvector changed_constraints; + rmm::device_uvector next_changed_constraints; + rmm::device_uvector changed_variables; + + rmm::device_uvector heavy_bounds_changed_agg; + rmm::device_uvector heavy_bounds_changed; + + struct view_t { + using f_t2 = typename type_2::type; + i_t* bounds_changed; + raft::device_span cnst_slack; + raft::device_span vars_bnd; + raft::device_span tmp_act; + raft::device_span changed_constraints; + raft::device_span next_changed_constraints; + raft::device_span changed_variables; + + raft::device_span heavy_bounds_changed_agg; + raft::device_span heavy_bounds_changed; + }; + + lb_bounds_update_data_t(lb_problem_t& problem); + void copy(lb_problem_t& problem); + void resize(lb_problem_t& problem); + void resize(const raft::handle_t* handle_ptr, + i_t n_constraints, + i_t n_variables, + i_t num_blocks_heavy_cnst, + i_t num_blocks_heavy_vars, + i_t num_heavy_vars); + void init_changed_constraints(const raft::handle_t* handle_ptr); + void disable_changed_constraints(const raft::handle_t* handle_ptr); + void prepare_for_next_iteration(const raft::handle_t* handle_ptr); + view_t view(); +}; + +} // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/presolve/lb_probing_cache.cu b/cpp/src/mip/presolve/lb_probing_cache.cu index 598a4c6bce..00c28d3450 100644 --- a/cpp/src/mip/presolve/lb_probing_cache.cu +++ b/cpp/src/mip/presolve/lb_probing_cache.cu @@ -201,9 +201,9 @@ __global__ void compute_min_slack_per_var(typename problem_t::view_t p if (std::signbit(a) != std::signbit(first_coeff)) { different_coeff = true; } auto cnst_idx = pb.reverse_constraints[var_offset + i]; auto cnstr_slack = cnst_slack[cnst_idx]; - auto delta_min_act = cnstr_slack.x + ((a < 0) ? a * ub : a * lb); + auto delta_min_act = get_lower(cnstr_slack) + ((a < 0) ? a * ub : a * lb); th_var_unit_slack = min(th_var_unit_slack, (delta_min_act / a)); - auto delta_max_act = cnstr_slack.y + ((a > 0) ? a * ub : a * lb); + auto delta_max_act = get_upper(cnstr_slack) + ((a > 0) ? a * ub : a * lb); th_var_unit_slack = min(th_var_unit_slack, (delta_max_act / a)); } __shared__ f_t shmem[raft::WarpSize]; @@ -232,7 +232,7 @@ __global__ void compute_min_slack_per_var(typename problem_t::view_t p th_max_excess = max(th_max_excess, excess); th_n_of_excess++; } - excess = max(0., cnstr_slack.y + diff); + excess = max(0., get_upper(cnstr_slack) + diff); if (excess > 0) { th_max_excess = max(th_max_excess, excess); th_n_of_excess++; diff --git a/cpp/src/mip/presolve/multi_probe.cu b/cpp/src/mip/presolve/multi_probe.cu index 8ff2fd871d..ccd3f19511 100644 --- a/cpp/src/mip/presolve/multi_probe.cu +++ b/cpp/src/mip/presolve/multi_probe.cu @@ -340,11 +340,12 @@ void multi_probe_t::update_host_bounds( rmm::device_uvector var_lb(variable_bounds.size(), handle_ptr->get_stream()); rmm::device_uvector var_ub(variable_bounds.size(), handle_ptr->get_stream()); - thrust::transform(handle_ptr->get_thrust_policy(), - variable_bounds.begin(), - variable_bounds.end(), - thrust::make_zip_iterator(thrust::make_tuple(var_lb.begin(), var_ub.begin())), - [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); + thrust::transform( + handle_ptr->get_thrust_policy(), + variable_bounds.begin(), + variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(var_lb.begin(), var_ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(get_lower(i), get_upper(i)); }); raft::copy(host_lb.data(), var_lb.data(), var_lb.size(), handle_ptr->get_stream()); raft::copy(host_ub.data(), var_ub.data(), var_ub.size(), handle_ptr->get_stream()); } @@ -362,12 +363,15 @@ void multi_probe_t::copy_problem_into_probing_buffers(problem_tget_thrust_policy(), - pb.variable_bounds.begin(), - pb.variable_bounds.end(), - thrust::make_zip_iterator(thrust::make_tuple( - upd_0.lb.begin(), upd_0.ub.begin(), upd_1.lb.begin(), upd_1.ub.begin())), - [] __device__(auto i) { return thrust::make_tuple(i.x, i.y, i.x, i.y); }); + thrust::transform( + handle_ptr->get_thrust_policy(), + pb.variable_bounds.begin(), + pb.variable_bounds.end(), + thrust::make_zip_iterator( + thrust::make_tuple(upd_0.lb.begin(), upd_0.ub.begin(), upd_1.lb.begin(), upd_1.ub.begin())), + [] __device__(auto i) { + return thrust::make_tuple(get_lower(i), get_upper(i), get_lower(i), get_upper(i)); + }); } template diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index 91f6636f0f..7389ec206f 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -171,15 +171,15 @@ void inline insert_current_probing_to_cache(i_t var_idx, cache_item.val_interval = probe_val; for (auto impacted_var_idx : h_integer_indices) { auto original_var_bounds = original_bounds[impacted_var_idx]; - if (original_var_bounds.x != modified_lb[impacted_var_idx] || - original_var_bounds.y != modified_ub[impacted_var_idx]) { + if (get_lower(original_var_bounds) != modified_lb[impacted_var_idx] || + get_upper(original_var_bounds) != modified_ub[impacted_var_idx]) { if (integer_equal( modified_lb[impacted_var_idx], modified_ub[impacted_var_idx], int_tol)) { ++n_implied_singletons; } - cuopt_assert(modified_lb[impacted_var_idx] >= original_var_bounds.x, + cuopt_assert(modified_lb[impacted_var_idx] >= get_lower(original_var_bounds), "Lower bound must be greater than or equal to original lower bound"); - cuopt_assert(modified_ub[impacted_var_idx] <= original_var_bounds.y, + cuopt_assert(modified_ub[impacted_var_idx] <= get_upper(original_var_bounds), "Upper bound must be less than or equal to original upper bound"); cached_bound_t new_bound{modified_lb[impacted_var_idx], modified_ub[impacted_var_idx]}; cache_item.var_to_cached_bound_map.insert({impacted_var_idx, new_bound}); @@ -211,8 +211,8 @@ __global__ void compute_min_slack_per_var(typename problem_t::view_t p i_t var_degree = pb.reverse_offsets[var_idx + 1] - var_offset; f_t th_var_unit_slack = std::numeric_limits::max(); auto var_bounds = pb.variable_bounds[var_idx]; - f_t lb = var_bounds.x; - f_t ub = var_bounds.y; + f_t lb = get_lower(var_bounds); + f_t ub = get_upper(var_bounds); f_t first_coeff = pb.reverse_coefficients[var_offset]; bool different_coeff = false; for (i_t i = threadIdx.x; i < var_degree; i += blockDim.x) { @@ -379,8 +379,8 @@ void compute_cache_for_var(i_t var_idx, std::vector h_improved_upper_bounds(h_var_bounds.size()); std::pair, val_interval_t> probe_vals; auto bounds = h_var_bounds[var_idx]; - f_t lb = bounds.x; - f_t ub = bounds.y; + f_t lb = get_lower(bounds); + f_t ub = get_upper(bounds); for (i_t i = 0; i < 2; ++i) { auto& probe_val = i == 0 ? probe_vals.first : probe_vals.second; // if binary, probe both values diff --git a/cpp/src/mip/presolve/trivial_presolve_helpers.cuh b/cpp/src/mip/presolve/trivial_presolve_helpers.cuh index ef5ce1af0e..cf7e5064d2 100644 --- a/cpp/src/mip/presolve/trivial_presolve_helpers.cuh +++ b/cpp/src/mip/presolve/trivial_presolve_helpers.cuh @@ -40,7 +40,7 @@ struct is_variable_free_t { { auto var = thrust::get<2>(edge); auto bounds = bnd[var]; - return abs(bounds.y - bounds.x) > tol; + return abs(get_upper(bounds) - get_lower(bounds)) > tol; } }; @@ -67,9 +67,10 @@ struct assign_fixed_var_t { __device__ void operator()(i_t i) const { if (!is_var_used[i]) { - auto orig_v_idx = variable_mapping[i]; - auto bounds = variable_bounds[i]; - fixed_assignment[orig_v_idx] = (objective_coefficients[i] > 0) ? bounds.x : bounds.y; + auto orig_v_idx = variable_mapping[i]; + auto bounds = variable_bounds[i]; + fixed_assignment[orig_v_idx] = + (objective_coefficients[i] > 0) ? get_lower(bounds) : get_upper(bounds); } } }; @@ -96,9 +97,9 @@ struct elem_multi_t { auto var = variables[i]; auto bounds = variable_bounds[var]; if (obj_coefficients[var] > 0) { - return bounds.x * coefficients[i]; + return get_lower(bounds) * coefficients[i]; } else { - return bounds.y * coefficients[i]; + return get_upper(bounds) * coefficients[i]; } } }; @@ -147,7 +148,7 @@ struct unused_var_obj_offset_t { // in case both bounds are infinite if (obj_coeff == 0.) return 0.; auto bounds = bnd[i]; - auto obj_off = (obj_coeff > 0) ? obj_coeff * bounds.x : obj_coeff * bounds.y; + auto obj_off = (obj_coeff > 0) ? obj_coeff * get_lower(bounds) : obj_coeff * get_upper(bounds); return var_map[i] ? 0. : obj_off; } }; diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 24c36d06d7..37064b5c63 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -411,7 +411,7 @@ void problem_t::check_problem_representation(bool check_transposed, thrust::make_counting_iterator(n_variables), [vars_bnd = make_span(variable_bounds)] __device__(i_t idx) { auto bounds = vars_bnd[idx]; - return bounds.x <= bounds.y; + return get_lower(bounds) <= get_upper(bounds); }), error_type_t::ValidationError, "Variable bounds are invalid"); @@ -561,23 +561,23 @@ void problem_t::check_problem_representation(bool check_transposed, return true; }), "Some variables aren't referenced in the appropriate indice tables"); - cuopt_assert( - thrust::all_of( - handle_ptr->get_thrust_policy(), - thrust::make_zip_iterator(thrust::make_counting_iterator(0), - is_binary_variable.cbegin()), - thrust::make_zip_iterator(thrust::make_counting_iterator(is_binary_variable.size()), + cuopt_assert(thrust::all_of(handle_ptr->get_thrust_policy(), + thrust::make_zip_iterator(thrust::make_counting_iterator(0), + is_binary_variable.cbegin()), + thrust::make_zip_iterator( + thrust::make_counting_iterator(is_binary_variable.size()), is_binary_variable.cend()), - [types = variable_types.data(), - vars_bnd = make_span(variable_bounds), - v = view()] __device__(const thrust::tuple tuple) { - i_t idx = thrust::get<0>(tuple); - i_t pred = thrust::get<1>(tuple); - auto bounds = vars_bnd[idx]; - return pred == (types[idx] != var_t::CONTINUOUS && v.integer_equal(bounds.x, 0.) && - v.integer_equal(bounds.y, 1.)); - }), - "The binary variable table is incorrect."); + [types = variable_types.data(), + vars_bnd = make_span(variable_bounds), + v = view()] __device__(const thrust::tuple tuple) { + i_t idx = thrust::get<0>(tuple); + i_t pred = thrust::get<1>(tuple); + auto bounds = vars_bnd[idx]; + return pred == (types[idx] != var_t::CONTINUOUS && + v.integer_equal(get_lower(bounds), 0.) && + v.integer_equal(get_upper(bounds), 1.)); + }), + "The binary variable table is incorrect."); if (!empty) { cuopt_assert(is_binary_pb == (n_variables == thrust::count(handle_ptr->get_thrust_policy(), is_binary_variable.begin(), @@ -751,15 +751,15 @@ void problem_t::compute_binary_var_table() auto pb_view = view(); is_binary_variable.resize(n_variables, handle_ptr->get_stream()); - thrust::tabulate( - handle_ptr->get_thrust_policy(), - is_binary_variable.begin(), - is_binary_variable.end(), - [pb_view] __device__(i_t i) { - auto bounds = pb_view.variable_bounds[i]; - return pb_view.variable_types[i] != var_t::CONTINUOUS && - (pb_view.integer_equal(bounds.x, 0) && pb_view.integer_equal(bounds.y, 1)); - }); + thrust::tabulate(handle_ptr->get_thrust_policy(), + is_binary_variable.begin(), + is_binary_variable.end(), + [pb_view] __device__(i_t i) { + auto bounds = pb_view.variable_bounds[i]; + return pb_view.variable_types[i] != var_t::CONTINUOUS && + (pb_view.integer_equal(get_lower(bounds), 0) && + pb_view.integer_equal(get_upper(bounds), 1)); + }); get_n_binary_variables(); binary_indices.resize(n_variables, handle_ptr->get_stream()); @@ -1337,8 +1337,8 @@ void standardize_bounds(std::vector>>& variable_ // but add only one var and use it in all constraints // TODO create one var for integrals and one var for continuous auto h_var_bound = h_var_bounds[i]; - if (h_var_bound.x == -std::numeric_limits::infinity() && - h_var_bound.y == std::numeric_limits::infinity()) { + if (get_lower(h_var_bound) == -std::numeric_limits::infinity() && + get_upper(h_var_bound) == std::numeric_limits::infinity()) { // add new variable auto var_coeff_vec = variable_constraint_map[i]; // negate all values in vec diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index 016d2c03b4..9d63e18579 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -135,9 +135,10 @@ class problem_t { DI bool check_variable_within_bounds(i_t v, f_t val) const { - const f_t int_tol = tolerances.integrality_tolerance; - auto bounds = variable_bounds[v]; - bool within_bounds = val <= (bounds.y + int_tol) && val >= (bounds.x - int_tol); + const f_t int_tol = tolerances.integrality_tolerance; + auto bounds = variable_bounds[v]; + bool within_bounds = + val <= (get_upper(bounds) + int_tol) && val >= (get_lower(bounds) - int_tol); return within_bounds; } @@ -161,14 +162,14 @@ class problem_t { auto bounds = variable_bounds[v]; f_t val; - if (isfinite(bounds.x) && isfinite(bounds.y)) { - f_t diff = bounds.y - bounds.x; - val = diff * rng.next_float() + bounds.x; + if (isfinite(get_lower(bounds)) && isfinite(get_upper(bounds))) { + f_t diff = get_upper(bounds) - get_lower(bounds); + val = diff * rng.next_float() + get_lower(bounds); } else { - auto finite_bound = isfinite(bounds.x) ? bounds.x : bounds.y; + auto finite_bound = isfinite(get_lower(bounds)) ? get_lower(bounds) : get_upper(bounds); val = finite_bound; } - cuopt_assert(isfinite(bounds.x), "Value must be finite"); + cuopt_assert(isfinite(get_lower(bounds)), "Value must be finite"); return val; } @@ -188,8 +189,6 @@ class problem_t { raft::device_span offsets; raft::device_span objective_coefficients; raft::device_span variable_bounds; - // raft::device_span variable_lower_bounds; - // raft::device_span variable_upper_bounds; raft::device_span constraint_lower_bounds; raft::device_span constraint_upper_bounds; raft::device_span variable_types; diff --git a/cpp/src/mip/problem/problem_helpers.cuh b/cpp/src/mip/problem/problem_helpers.cuh index ffbd1930be..8451983a99 100644 --- a/cpp/src/mip/problem/problem_helpers.cuh +++ b/cpp/src/mip/problem/problem_helpers.cuh @@ -58,7 +58,6 @@ struct transform_bounds_functor { } }; -#if 1 template static void set_variable_bounds(detail::problem_t& op_problem) { @@ -88,7 +87,6 @@ static void set_variable_bounds(detail::problem_t& op_problem) vars_bnd[i] = f_t2{lb, ub}; }); } -#endif template static void set_bounds_if_not_set(detail::problem_t& op_problem) @@ -122,29 +120,7 @@ static void set_bounds_if_not_set(detail::problem_t& op_problem) transform_bounds_functor()); } -#if 0 - // If variable bound was not set, set it to default value - if (op_problem.variable_lower_bounds.is_empty() && - !op_problem.objective_coefficients.is_empty()) { - op_problem.variable_lower_bounds.resize(op_problem.objective_coefficients.size(), - op_problem.handle_ptr->get_stream()); - thrust::fill(op_problem.handle_ptr->get_thrust_policy(), - op_problem.variable_lower_bounds.begin(), - op_problem.variable_lower_bounds.end(), - f_t(0)); - } - if (op_problem.variable_upper_bounds.is_empty() && - !op_problem.objective_coefficients.is_empty()) { - op_problem.variable_upper_bounds.resize(op_problem.objective_coefficients.size(), - op_problem.handle_ptr->get_stream()); - thrust::fill(op_problem.handle_ptr->get_thrust_policy(), - op_problem.variable_upper_bounds.begin(), - op_problem.variable_upper_bounds.end(), - std::numeric_limits::infinity()); - } -#else set_variable_bounds(op_problem); -#endif if (op_problem.variable_types.is_empty() && !op_problem.objective_coefficients.is_empty()) { op_problem.variable_types.resize(op_problem.objective_coefficients.size(), op_problem.handle_ptr->get_stream()); @@ -301,7 +277,7 @@ static bool check_var_bounds_sanity(const detail::problem_t& problem) [tolerance = problem.tolerances.presolve_absolute_tolerance, var_bnd = make_span(problem.variable_bounds)] __device__(i_t index) { auto var_bounds = var_bnd[index]; - return (var_bounds.x > var_bounds.y + tolerance); + return (get_lower(var_bounds) > get_upper(var_bounds) + tolerance); }); return !crossing_bounds_detected; } @@ -333,7 +309,7 @@ static void round_bounds(detail::problem_t& problem) if (types[index] == var_t::INTEGER) { using f_t2 = typename type_2::type; auto bnd = bounds[index]; - bounds[index] = f_t2{ceil(bnd.x), floor(bnd.y)}; + bounds[index] = f_t2{ceil(get_lower(bnd)), floor(get_upper(bnd))}; } }); } diff --git a/cpp/src/mip/solution/feasibility_test.cuh b/cpp/src/mip/solution/feasibility_test.cuh index cde7eafbd5..5c452e45fd 100644 --- a/cpp/src/mip/solution/feasibility_test.cuh +++ b/cpp/src/mip/solution/feasibility_test.cuh @@ -55,8 +55,8 @@ __global__ void test_variable_bounds_kernel(typename solution_t::view_ printf("inf var %d val %f l %f u %f integer %d\n", v, val, - sol.problem.variable_bounds[v].x, - sol.problem.variable_bounds[v].y, + get_lower(sol.problem.variable_bounds[v]), + get_upper(sol.problem.variable_bounds[v]), sol.problem.is_integer_var(v)); } cuopt_assert(isfinite(val), "assignment should be finite!"); @@ -72,8 +72,8 @@ __global__ void test_variable_bounds_kernel(typename solution_t::view_ printf("oob var %d val %f l %f u %f integer %d\n", v, val, - sol.problem.variable_bounds[v].x, - sol.problem.variable_bounds[v].y, + get_lower(sol.problem.variable_bounds[v]), + get_upper(sol.problem.variable_bounds[v]), sol.problem.is_integer_var(v)); } cuopt_assert(feasible, "Variables should be feasible"); diff --git a/cpp/src/mip/solution/solution.cu b/cpp/src/mip/solution/solution.cu index 0f6e000f4a..7c95aaeed2 100644 --- a/cpp/src/mip/solution/solution.cu +++ b/cpp/src/mip/solution/solution.cu @@ -36,8 +36,8 @@ namespace cuopt::linear_programming::detail { template -rmm::device_uvector get_lower_bounds(rmm::device_uvector::type>& bounds, - const raft::handle_t* handle_ptr) +rmm::device_uvector get_lower_bounds( + rmm::device_uvector::type> const& bounds, const raft::handle_t* handle_ptr) { using f_t2 = typename type_2::type; rmm::device_uvector lower_bounds(bounds.size(), handle_ptr->get_stream()); @@ -242,8 +242,8 @@ void solution_t::assign_random_within_bounds(f_t ratio_of_vars_to_rand bool skip = unif_prob(rng) > ratio_of_vars_to_random_assign; if (skip) { continue; } auto var_bounds = variable_bounds[i]; - auto lower_bound = var_bounds.x; - auto upper_bound = var_bounds.y; + auto lower_bound = get_lower(var_bounds); + auto upper_bound = get_upper(var_bounds); if (lower_bound == -std::numeric_limits::infinity()) { h_assignment[i] = upper_bound; } else if (upper_bound == std::numeric_limits::infinity()) { @@ -460,8 +460,8 @@ i_t solution_t::calculate_similarity_radius(solution_t& othe auto var_bounds = p_view.variable_bounds[idx]; return diverse_equal(other_ptr[idx], curr_assignment[idx], - var_bounds.x, - var_bounds.y, + get_lower(var_bounds), + get_upper(var_bounds), p_view.is_integer_var(idx), p_view.tolerances.integrality_tolerance); })); @@ -564,8 +564,8 @@ f_t solution_t::compute_max_variable_violation() thrust::make_counting_iterator(0) + problem_ptr->n_variables, cuda::proclaim_return_type([v = view()] __device__(i_t idx) -> f_t { auto var_bounds = v.problem.variable_bounds[idx]; - f_t lower_vio = max(0., var_bounds.x - v.assignment[idx]); - f_t upper_vio = max(0., v.assignment[idx] - var_bounds.y); + f_t lower_vio = max(0., get_lower(var_bounds) - v.assignment[idx]); + f_t upper_vio = max(0., v.assignment[idx] - get_upper(var_bounds)); return max(lower_vio, upper_vio); }), 0., diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index fee1fd9b01..9c76a3e10c 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -82,7 +82,8 @@ mip_solution_t run_mip(detail::problem_t& problem, thrust::make_counting_iterator(problem.n_variables), [sol = solution.assignment.data(), pb = problem.view()] __device__(i_t index) { auto bounds = pb.variable_bounds[index]; - sol[index] = pb.objective_coefficients[index] > 0 ? bounds.x : bounds.y; + sol[index] = pb.objective_coefficients[index] > 0 ? get_lower(bounds) + : get_upper(bounds); }); problem.post_process_solution(solution); solution.compute_objective(); // just to ensure h_user_obj is set diff --git a/cpp/src/mip/utils.cuh b/cpp/src/mip/utils.cuh index 734cf10b6b..47f1bbc48b 100644 --- a/cpp/src/mip/utils.cuh +++ b/cpp/src/mip/utils.cuh @@ -259,10 +259,10 @@ void clamp_within_var_bounds(rmm::device_uvector& assignment, thrust::make_counting_iterator(0) + problem_ptr->n_variables, [assignment_ptr, variable_bound = problem_ptr->variable_bounds.data()] __device__(i_t idx) { auto bound = variable_bound[idx]; - if (assignment_ptr[idx] < bound.x) { - assignment_ptr[idx] = bound.x; - } else if (assignment_ptr[idx] > bound.y) { - assignment_ptr[idx] = bound.y; + if (assignment_ptr[idx] < get_lower(bound)) { + assignment_ptr[idx] = get_lower(bound); + } else if (assignment_ptr[idx] > get_upper(bound)) { + assignment_ptr[idx] = get_upper(bound); } }); } diff --git a/cpp/src/utilities/copy_helpers.hpp b/cpp/src/utilities/copy_helpers.hpp index 65396ba585..7207ad72aa 100644 --- a/cpp/src/utilities/copy_helpers.hpp +++ b/cpp/src/utilities/copy_helpers.hpp @@ -69,11 +69,26 @@ struct scalar_type { using type = double; }; +template <> +struct scalar_type { + using type = const int; +}; + +template <> +struct scalar_type { + using type = const float; +}; + +template <> +struct scalar_type { + using type = const double; +}; + template raft::device_span::type> make_span_2(rmm::device_uvector& container) { - // TODO : ceildiv or throw assert using T2 = typename type_2::type; + static_assert(sizeof(T2) == 2 * sizeof(T)); return raft::device_span(reinterpret_cast(container.data()), sizeof(T) * container.size() / sizeof(T2)); } @@ -82,12 +97,24 @@ template raft::device_span::type> make_span_2( rmm::device_uvector const& container) { - // TODO : ceildiv or throw assert using T2 = typename type_2::type; + static_assert(sizeof(T2) == 2 * sizeof(T)); return raft::device_span(reinterpret_cast(container.data()), sizeof(T) * container.size() / sizeof(T2)); } +template +__host__ __device__ inline typename scalar_type::type& get_lower(f_t2& val) +{ + return val.x; +} + +template +__host__ __device__ inline typename scalar_type::type& get_upper(f_t2& val) +{ + return val.y; +} + /** * @brief Simple utility function to copy device ptr to host * @@ -320,11 +347,12 @@ std::tuple, std::vector> extract_host_bounds( { rmm::device_uvector var_lb(variable_bounds.size(), handle_ptr->get_stream()); rmm::device_uvector var_ub(variable_bounds.size(), handle_ptr->get_stream()); - thrust::transform(handle_ptr->get_thrust_policy(), - variable_bounds.begin(), - variable_bounds.end(), - thrust::make_zip_iterator(thrust::make_tuple(var_lb.begin(), var_ub.begin())), - [] __device__(auto i) { return thrust::make_tuple(i.x, i.y); }); + thrust::transform( + handle_ptr->get_thrust_policy(), + variable_bounds.begin(), + variable_bounds.end(), + thrust::make_zip_iterator(thrust::make_tuple(var_lb.begin(), var_ub.begin())), + [] __device__(auto i) { return thrust::make_tuple(get_lower(i), get_upper(i)); }); handle_ptr->sync_stream(); auto h_var_lb = cuopt::host_copy(var_lb); auto h_var_ub = cuopt::host_copy(var_ub); From e062f51979f0047194dc653515bb5ae2cbda3f60 Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Fri, 5 Sep 2025 13:58:11 -0400 Subject: [PATCH 09/10] add back sync --- cpp/src/mip/diversity/diversity_manager.cu | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 7fdaef31e9..726eb5b41a 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -320,6 +320,7 @@ bool diversity_manager_t::run_presolve(f_t time_limit) } stats.presolve_time = presolve_timer.elapsed_time(); lp_optimal_solution.resize(problem_ptr->n_variables, problem_ptr->handle_ptr->get_stream()); + problem_ptr->handle_ptr->sync_stream(); return true; } From c9edb5a68829bd2b8ba1d0510dbe5f3f6d8b163c Mon Sep 17 00:00:00 2001 From: Kumar Aatish Date: Tue, 9 Sep 2025 09:56:21 -0400 Subject: [PATCH 10/10] pr review fixes --- .../local_search/rounding/constraint_prop.cu | 8 --- .../mip/presolve/lb_bounds_udpate_data.cuh | 69 ------------------- cpp/src/mip/problem/host_helper.cuh | 6 +- 3 files changed, 1 insertion(+), 82 deletions(-) delete mode 100644 cpp/src/mip/presolve/lb_bounds_udpate_data.cuh diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index e250cb7554..61e8e08675 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -741,14 +741,6 @@ void constraint_prop_t::update_host_assignment(const solution_tget_stream()); } -template -struct extract_bounds_t { - __device__ thrust::tuple operator()(f_t2 bounds) - { - return thrust::make_tuple(get_lower(bounds), get_upper(bounds)); - } -}; - template void constraint_prop_t::set_host_bounds(const solution_t& sol) { diff --git a/cpp/src/mip/presolve/lb_bounds_udpate_data.cuh b/cpp/src/mip/presolve/lb_bounds_udpate_data.cuh deleted file mode 100644 index ad667aac11..0000000000 --- a/cpp/src/mip/presolve/lb_bounds_udpate_data.cuh +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights - * reserved. SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -namespace cuopt::linear_programming::detail { - -template -struct lb_bounds_update_data_t { - rmm::device_scalar bounds_changed; - rmm::device_uvector cnst_slack; - rmm::device_uvector vars_bnd; - rmm::device_uvector tmp_act; - rmm::device_uvector changed_constraints; - rmm::device_uvector next_changed_constraints; - rmm::device_uvector changed_variables; - - rmm::device_uvector heavy_bounds_changed_agg; - rmm::device_uvector heavy_bounds_changed; - - struct view_t { - using f_t2 = typename type_2::type; - i_t* bounds_changed; - raft::device_span cnst_slack; - raft::device_span vars_bnd; - raft::device_span tmp_act; - raft::device_span changed_constraints; - raft::device_span next_changed_constraints; - raft::device_span changed_variables; - - raft::device_span heavy_bounds_changed_agg; - raft::device_span heavy_bounds_changed; - }; - - lb_bounds_update_data_t(lb_problem_t& problem); - void copy(lb_problem_t& problem); - void resize(lb_problem_t& problem); - void resize(const raft::handle_t* handle_ptr, - i_t n_constraints, - i_t n_variables, - i_t num_blocks_heavy_cnst, - i_t num_blocks_heavy_vars, - i_t num_heavy_vars); - void init_changed_constraints(const raft::handle_t* handle_ptr); - void disable_changed_constraints(const raft::handle_t* handle_ptr); - void prepare_for_next_iteration(const raft::handle_t* handle_ptr); - view_t view(); -}; - -} // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/problem/host_helper.cuh b/cpp/src/mip/problem/host_helper.cuh index 297a827294..9136e6899e 100644 --- a/cpp/src/mip/problem/host_helper.cuh +++ b/cpp/src/mip/problem/host_helper.cuh @@ -56,22 +56,18 @@ struct variables_delta_t { using f_t2 = typename type_2::type; std::vector objective_coefficients; std::vector variable_bounds; - std::vector lower_bounds; - std::vector upper_bounds; std::vector variable_types; std::vector is_binary_variable; i_t n_vars; - i_t size() const { return lower_bounds.size(); } + i_t size() const { return variable_bounds.size(); } // returns the added variable id i_t add_variable(f_t lower_bound, f_t upper_bound, f_t obj_weight, var_t var_type) { cuopt_assert(lower_bound >= 0, "Variable bounds must be non-negative!"); variable_bounds.push_back(f_t2{lower_bound, upper_bound}); - lower_bounds.push_back(lower_bound); - upper_bounds.push_back(upper_bound); objective_coefficients.push_back(obj_weight); variable_types.push_back(var_type); is_binary_variable.push_back(0);