Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cpp/include/cuopt/logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ inline rapids_logger::logger& default_logger()
rapids_logger::logger logger_{"CUOPT", {default_sink()}};
logger_.set_pattern(default_pattern());
logger_.set_level(default_level());
logger_.flush_on(rapids_logger::level_enum::info);

return logger_;
}();
Expand All @@ -91,6 +92,7 @@ inline void reset_default_logger()
default_logger().sinks().push_back(default_sink());
default_logger().set_pattern(default_pattern());
default_logger().set_level(default_level());
default_logger().flush_on(rapids_logger::level_enum::info);
}

} // namespace cuopt
34 changes: 16 additions & 18 deletions cpp/src/mip/diversity/diversity_manager.cu
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,12 @@ void diversity_manager_t<i_t, f_t>::add_user_given_solution(
if (context.settings.has_initial_solution()) {
solution_t<i_t, f_t> sol(*problem_ptr);
auto& init_sol = context.settings.get_initial_solution();
if (sol.assignment.size() <= init_sol.size()) {
thrust::for_each(
sol.handle_ptr->get_thrust_policy(),
thrust::make_counting_iterator(0),
thrust::make_counting_iterator((int)sol.assignment.size()),
[assgn = make_span(sol.assignment),
init_sol = make_span(init_sol),
var_map = make_span(problem_ptr->presolve_data.variable_mapping)] __device__(i_t i) {
assgn[i] = init_sol[var_map[i]];
});
rmm::device_uvector<f_t> init_sol_assignment(init_sol, sol.handle_ptr->get_stream());
if (problem_ptr->pre_process_assignment(init_sol_assignment)) {
raft::copy(sol.assignment.data(),
init_sol_assignment.data(),
init_sol_assignment.size(),
sol.handle_ptr->get_stream());
bool is_feasible = sol.compute_feasibility();
cuopt_func_call(sol.test_variable_bounds(true));
CUOPT_LOG_INFO("Adding initial solution success! feas %d objective %f excess %f",
Expand All @@ -168,8 +164,8 @@ void diversity_manager_t<i_t, f_t>::add_user_given_solution(
} else {
CUOPT_LOG_ERROR(
"Error cannot add the provided initial solution! \
Assignment size %lu \
initial solution size %lu",
Assignment size %lu \
initial solution size %lu",
sol.assignment.size(),
init_sol.size());
}
Expand Down Expand Up @@ -250,14 +246,16 @@ bool diversity_manager_t<i_t, f_t>::run_presolve(f_t time_limit)
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); }
}
if (!problem_ptr->empty) {
// do the resizing no-matter what, bounds presolve might not change the bounds but initial
// trivial presolve might have
ls.constraint_prop.bounds_update.resize(*problem_ptr);
ls.constraint_prop.conditional_bounds_update.update_constraint_bounds(
*problem_ptr, ls.constraint_prop.bounds_update);
check_bounds_sanity(*problem_ptr);
}
// do the resizing no-matter what, bounds presolve might not change the bounds but initial trivial
// presolve might have
ls.constraint_prop.bounds_update.resize(*problem_ptr);
ls.constraint_prop.conditional_bounds_update.update_constraint_bounds(
*problem_ptr, ls.constraint_prop.bounds_update);
check_bounds_sanity(*problem_ptr);
stats.presolve_time = presolve_timer.elapsed_time();
return true;
}
Expand Down
44 changes: 28 additions & 16 deletions cpp/src/mip/diversity/population.cu
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ void population_t<i_t, f_t>::run_solution_callbacks(solution_t<i_t, f_t>& sol)
? sol.get_user_objective() > best_feasible_objective
: sol.get_user_objective() < best_feasible_objective;
auto user_callbacks = context.settings.get_mip_callbacks();

if (better_solution_found && sol.get_feasible()) {
CUOPT_LOG_DEBUG("Population: Found new best solution %g", sol.get_user_objective());
best_feasible_objective = sol.get_user_objective();
Expand All @@ -174,19 +173,29 @@ void population_t<i_t, f_t>::run_solution_callbacks(solution_t<i_t, f_t>& sol)
for (auto callback : user_callbacks) {
if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) {
auto get_sol_callback = static_cast<internals::get_solution_callback_t*>(callback);
rmm::device_uvector<f_t> incumbent_assignment(sol.assignment, sol.handle_ptr->get_stream());
problem_ptr->post_process_assignment(incumbent_assignment);
rmm::device_uvector<f_t> dummy(0, sol.handle_ptr->get_stream());
solution_t<i_t, f_t> temp_sol(sol);
problem_ptr->post_process_assignment(temp_sol.assignment);
rmm::device_uvector<f_t> dummy(0, temp_sol.handle_ptr->get_stream());
if (context.settings.mip_scaling) {
context.scaling.unscale_solutions(incumbent_assignment, dummy);
context.scaling.unscale_solutions(temp_sol.assignment, dummy);
// Need to get unscaled problem as well
problem_t<i_t, f_t> n_problem(*sol.problem_ptr->original_problem_ptr);
temp_sol.problem_ptr = &n_problem;
temp_sol.resize_to_original_problem();
temp_sol.compute_feasibility();
if (!temp_sol.get_feasible()) {
CUOPT_LOG_DEBUG("Discard infeasible after unscaling");
return;
}
}

rmm::device_uvector<f_t> user_objective_vec(1, sol.handle_ptr->get_stream());
rmm::device_uvector<f_t> user_objective_vec(1, temp_sol.handle_ptr->get_stream());

f_t user_objective = sol.problem_ptr->get_user_obj_from_solver_obj(sol.get_objective());
user_objective_vec.set_element_async(0, user_objective, sol.handle_ptr->get_stream());
f_t user_objective =
temp_sol.problem_ptr->get_user_obj_from_solver_obj(temp_sol.get_objective());
user_objective_vec.set_element_async(0, user_objective, temp_sol.handle_ptr->get_stream());
CUOPT_LOG_DEBUG("Returning incumbent solution with objective %g", user_objective);
get_sol_callback->get_solution(incumbent_assignment.data(), user_objective_vec.data());
get_sol_callback->get_solution(temp_sol.assignment.data(), user_objective_vec.data());
}
}
}
Expand All @@ -199,32 +208,35 @@ void population_t<i_t, f_t>::run_solution_callbacks(solution_t<i_t, f_t>& sol)
rmm::device_uvector<f_t> dummy(0, sol.handle_ptr->get_stream());
solution_t<i_t, f_t> outside_sol(sol);
rmm::device_scalar<f_t> d_outside_sol_objective(sol.handle_ptr->get_stream());
auto inf = std::numeric_limits<f_t>::infinity();
d_outside_sol_objective.set_value_async(inf, sol.handle_ptr->get_stream());
sol.handle_ptr->sync_stream();
set_sol_callback->set_solution(incumbent_assignment.data(), d_outside_sol_objective.data());

f_t outside_sol_objective = d_outside_sol_objective.value(sol.handle_ptr->get_stream());

// The callback might be called without setting any valid solution or objective which triggers
// asserts
if (outside_sol_objective == inf) { return; }
CUOPT_LOG_DEBUG("Injecting external solution with objective %g", outside_sol_objective);

if (context.settings.mip_scaling) {
context.scaling.scale_solutions(incumbent_assignment, dummy);
}
bool is_valid = problem_ptr->pre_process_assignment(incumbent_assignment);
if (!is_valid) { return; }

cuopt_assert(outside_sol.assignment.size() == incumbent_assignment.size(),
"Incumbent assignment size mismatch");
raft::copy(outside_sol.assignment.data(),
incumbent_assignment.data(),
incumbent_assignment.size(),
sol.handle_ptr->get_stream());

outside_sol.compute_feasibility();

CUOPT_LOG_DEBUG("Injected solution feasibility = %d", outside_sol.get_feasible());
CUOPT_LOG_DEBUG("Injected solution feasibility = %d objective = %g",
outside_sol.get_feasible(),
outside_sol.get_user_objective());

cuopt_assert(std::abs(outside_sol.problem_ptr->get_user_obj_from_solver_obj(
outside_sol.get_objective()) -
outside_sol_objective) <= 1e-6,
cuopt_assert(std::abs(outside_sol.get_user_objective() - outside_sol_objective) <= 1e-6,
"External solution objective mismatch");
auto h_outside_sol = outside_sol.get_host_assignment();
add_external_solution(h_outside_sol, outside_sol.get_objective());
Expand Down
5 changes: 3 additions & 2 deletions cpp/src/mip/presolve/trivial_presolve.cuh
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,9 @@ void update_from_csr(problem_t<i_t, f_t>& pb)

pb.n_constraints = updated_n_cnst;
pb.n_variables = updated_n_vars;
CUOPT_LOG_INFO(
"After trivial presolve updated %d constraints %d variables", updated_n_cnst, updated_n_vars);
CUOPT_LOG_INFO("After trivial presolve updated number of %d constraints %d variables",
updated_n_cnst,
updated_n_vars);
// check successive cnst in coo increases by atmost 1
// update csr offset
pb.offsets.resize(pb.n_constraints + 1, handle_ptr->get_stream());
Expand Down
73 changes: 38 additions & 35 deletions cpp/src/mip/problem/problem.cu
Original file line number Diff line number Diff line change
Expand Up @@ -593,42 +593,42 @@ bool problem_t<i_t, f_t>::pre_process_assignment(rmm::device_uvector<f_t>& assig
return false;
}

// Map assignment to internal solution using variable mapping
rmm::device_uvector<f_t> internal_assignment(presolve_data.variable_mapping.size(),
handle_ptr->get_stream());
thrust::gather(handle_ptr->get_thrust_policy(),
presolve_data.variable_mapping.begin(),
presolve_data.variable_mapping.end(),
assignment.begin(),
internal_assignment.begin());

// create a temp assignment with the var size after bounds standardization (free vars added)
rmm::device_uvector<f_t> temp_assignment(presolve_data.additional_var_used.size(),
handle_ptr->get_stream());
// copy the assignment to the first part(the original variable count) of the temp_assignment
raft::copy(
temp_assignment.data(), assignment.data(), assignment.size(), handle_ptr->get_stream());
auto d_additional_var_used =
cuopt::device_copy(presolve_data.additional_var_used, handle_ptr->get_stream());
auto d_additional_var_id_per_var =
cuopt::device_copy(presolve_data.additional_var_id_per_var, handle_ptr->get_stream());

thrust::for_each(
handle_ptr->get_thrust_policy(),
thrust::make_counting_iterator<i_t>(0),
thrust::make_counting_iterator<i_t>(presolve_data.variable_mapping.size()),
[additional_var_used = d_additional_var_used.data(),
additional_var_id_per_var = d_additional_var_id_per_var.data(),
assgn = internal_assignment.data()] __device__(auto idx) {
if (additional_var_used[idx]) {
cuopt_assert(additional_var_id_per_var[idx] != -1, "additional_var_id_per_var is not set");
// We have two non-negative variables y and z that simulate a free variable x. If the value
// of x is negative, we can set z to be something higher than y. If the value of x is
// positive we can set y greater than z
assgn[additional_var_id_per_var[idx]] = assgn[idx] < 0 ? -assgn[idx] : assgn[idx];
assgn[idx] += assgn[additional_var_id_per_var[idx]];
}
});
assignment.resize(internal_assignment.size(), handle_ptr->get_stream());
// handle free var logic by substituting the free vars and their corresponding vars
thrust::for_each(handle_ptr->get_thrust_policy(),
thrust::make_counting_iterator<i_t>(0),
thrust::make_counting_iterator<i_t>(original_problem_ptr->get_n_variables()),
[additional_var_used = d_additional_var_used.data(),
additional_var_id_per_var = d_additional_var_id_per_var.data(),
assgn = temp_assignment.data()] __device__(auto idx) {
if (additional_var_used[idx]) {
cuopt_assert(additional_var_id_per_var[idx] != -1,
"additional_var_id_per_var is not set");
// We have two non-negative variables y and z that simulate a free variable
// x. If the value of x is negative, we can set z to be something higher than
// y. If the value of x is positive we can set y greater than z
assgn[additional_var_id_per_var[idx]] = (assgn[idx] < 0 ? -assgn[idx] : 0.);
assgn[idx] += assgn[additional_var_id_per_var[idx]];
}
});
assignment.resize(n_variables, handle_ptr->get_stream());
assignment.shrink_to_fit(handle_ptr->get_stream());
raft::copy(assignment.data(),
internal_assignment.data(),
internal_assignment.size(),
handle_ptr->get_stream());
cuopt_assert(presolve_data.variable_mapping.size() == n_variables, "size mismatch");
thrust::gather(handle_ptr->get_thrust_policy(),
presolve_data.variable_mapping.begin(),
presolve_data.variable_mapping.end(),
temp_assignment.begin(),
assignment.begin());
handle_ptr->sync_stream();
return true;
}
Expand All @@ -643,11 +643,14 @@ void problem_t<i_t, f_t>::post_process_assignment(rmm::device_uvector<f_t>& curr
auto assgn = make_span(current_assignment);
auto fixed_assgn = make_span(presolve_data.fixed_var_assignment);
auto var_map = make_span(presolve_data.variable_mapping);
thrust::for_each(
handle_ptr->get_thrust_policy(),
thrust::make_counting_iterator<i_t>(0),
thrust::make_counting_iterator<i_t>(current_assignment.size()),
[fixed_assgn, var_map, assgn] __device__(auto idx) { fixed_assgn[var_map[idx]] = assgn[idx]; });
if (current_assignment.size() > 0) {
thrust::for_each(handle_ptr->get_thrust_policy(),
thrust::make_counting_iterator<i_t>(0),
thrust::make_counting_iterator<i_t>(current_assignment.size()),
[fixed_assgn, var_map, assgn] __device__(auto idx) {
fixed_assgn[var_map[idx]] = assgn[idx];
});
}
expand_device_copy(
current_assignment, presolve_data.fixed_var_assignment, handle_ptr->get_stream());
auto h_assignment = cuopt::host_copy(current_assignment, handle_ptr->get_stream());
Expand Down
8 changes: 7 additions & 1 deletion cpp/src/mip/solver.cu
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ solution_t<i_t, f_t> mip_solver_t<i_t, f_t>::run_solver()
"preprocess_problem should be called before running the solver");

if (context.problem_ptr->empty) {
CUOPT_LOG_INFO("Problem fully reduced at presolve");
CUOPT_LOG_INFO("Problem fully reduced at trivial presolve");
solution_t<i_t, f_t> sol(*context.problem_ptr);
context.problem_ptr->post_process_solution(sol);
return sol;
Expand All @@ -112,6 +112,12 @@ solution_t<i_t, f_t> mip_solver_t<i_t, f_t>::run_solver()
context.problem_ptr->post_process_solution(sol);
return sol;
}
if (context.problem_ptr->empty) {
CUOPT_LOG_INFO("Problem fully reduced at presolve");
solution_t<i_t, f_t> sol(*context.problem_ptr);
context.problem_ptr->post_process_solution(sol);
return sol;
}

namespace dual_simplex = cuopt::linear_programming::dual_simplex;
std::future<dual_simplex::mip_status_t> branch_and_bound_status_future;
Expand Down
13 changes: 8 additions & 5 deletions cpp/tests/mip/incumbent_callback_test.cu
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,16 @@ class test_set_solution_callback_t : public cuopt::internals::set_solution_callb
// This will check that the we are able to recompute our own solution
void set_solution(void* data, void* cost) override
{
n_calls++;
rmm::cuda_stream_view stream{};
auto assignment = static_cast<double*>(data);
auto cost_ptr = static_cast<double*>(cost);
auto assignment = static_cast<double*>(data);
auto cost_ptr = static_cast<double*>(cost);
if (solutions.empty()) { return; }

auto const& [last_assignment, last_cost] = solutions.back();
raft::copy(assignment, last_assignment.data(), last_assignment.size(), stream);
raft::copy(cost_ptr, &last_cost, 1, stream);
stream.synchronize();
n_calls++;
}
std::vector<std::pair<rmm::device_uvector<double>, double>>& solutions;
int n_calls;
Expand All @@ -74,14 +76,14 @@ class test_get_solution_callback_t : public cuopt::internals::get_solution_callb
}
void get_solution(void* data, void* cost) override
{
n_calls++;
rmm::cuda_stream_view stream{};
rmm::device_uvector<double> assignment(n_variables, stream);
raft::copy(assignment.data(), static_cast<double*>(data), n_variables, stream);
auto h_cost = 0.;
raft::copy(&h_cost, static_cast<double*>(cost), 1, stream);
stream.synchronize();
solutions.push_back(std::make_pair(std::move(assignment), h_cost));
n_calls++;
}
std::vector<std::pair<rmm::device_uvector<double>, double>>& solutions;
int n_calls;
Expand Down Expand Up @@ -131,7 +133,8 @@ void test_incumbent_callback(std::string test_instance)

TEST(mip_solve, incumbent_callback_test)
{
std::vector<std::string> test_instances = {"mip/50v-10.mps", "mip/neos5.mps", "mip/swath1.mps"};
std::vector<std::string> test_instances = {
"mip/50v-10.mps", "mip/neos5-free-bound.mps", "mip/swath1.mps"};
for (const auto& test_instance : test_instances) {
test_incumbent_callback(test_instance);
}
Expand Down
2 changes: 1 addition & 1 deletion cpp/tests/mip/server_test.cu
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

#include "../utilities/pdlp_test_utilities.cuh"
#include "../linear_programming/utilities/pdlp_test_utilities.cuh"
#include "mip_utils.cuh"

#include <cuopt/linear_programming/solve.hpp>
Expand Down