Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1bf2309
first merge: merging only the mip directory
akifcorduk Jul 1, 2025
d49d073
add more changes from include directory
akifcorduk Jul 1, 2025
542e4b4
fix merge conflicts
akifcorduk Jul 1, 2025
6af2454
fix merge conflicts
akifcorduk Jul 2, 2025
24cbd7c
fix more compile errors
akifcorduk Jul 2, 2025
18bfa3c
remove patch files
akifcorduk Jul 2, 2025
804d3d5
remove patch files
akifcorduk Jul 2, 2025
4040daf
remove more unnecessary code
akifcorduk Jul 2, 2025
345cc5e
log to console by default
akifcorduk Jul 2, 2025
ff42e2c
add default lp state for fixed and normal problem
akifcorduk Jul 3, 2025
f77e436
add var_t header to host_helpers
akifcorduk Jul 3, 2025
3200fb8
Merge branch 'branch-25.08' of github.com:NVIDIA/cuopt into new_heuri…
akifcorduk Jul 4, 2025
d485ca8
implement MAB for LS and fix a bug
akifcorduk Jul 8, 2025
404763c
fix few bugs and add checks
akifcorduk Jul 8, 2025
d2a704e
add missing header
akifcorduk Jul 8, 2025
8207b2d
fix bugs in MAB and improve the normalization logic
akifcorduk Jul 9, 2025
4559128
higher recency
akifcorduk Jul 9, 2025
bed3ea2
revert majority of the settings without the LS MAB
akifcorduk Jul 14, 2025
957eccd
with LS MAB
akifcorduk Jul 14, 2025
ed5e347
fix logging with LS MAB
akifcorduk Jul 14, 2025
be7bfb9
without LS MAB
akifcorduk Jul 14, 2025
53a802d
revert more mab changes
akifcorduk Jul 14, 2025
0a84c94
revert logging changes
akifcorduk Jul 14, 2025
f3457a1
by default check for both iter and time limit
akifcorduk Jul 14, 2025
e1ab729
enable mab for LS
akifcorduk Jul 15, 2025
a8a2fef
disable mab ls
akifcorduk Jul 15, 2025
00a51c8
implement solution hash check in local search
akifcorduk Jul 16, 2025
767828d
lower tolerance
akifcorduk Jul 16, 2025
9d3c128
add dual simplex root relaxation to heuristics
akifcorduk Jul 17, 2025
55bc1e2
implement stopping of root pdlp if simplex is found
akifcorduk Jul 17, 2025
08aab8e
Merge branch 'branch-25.08' of github.com:NVIDIA/cuopt into mab_for_ls
akifcorduk Jul 17, 2025
03ade15
fix few merge conflicts
akifcorduk Jul 17, 2025
2adeb6b
use set device in case there are other gpus in the system
akifcorduk Jul 17, 2025
aceb680
fix user bound issues
akifcorduk Jul 17, 2025
d33c4d8
Merge branch 'branch-25.08' into mab_for_ls
rgsl888prabhu Jul 17, 2025
9074349
handle review comments
akifcorduk Jul 18, 2025
6bcd726
Merge branch 'mab_for_ls' of github.com:akifcorduk/cuopt into mab_for_ls
akifcorduk Jul 18, 2025
08d8372
add objective check
akifcorduk Jul 18, 2025
22ac5a5
add static assert for double precision
akifcorduk Jul 18, 2025
a92b9d4
disable the batch python tests
akifcorduk Jul 18, 2025
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 benchmarks/linear_programming/cuopt/run_mip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ int run_single_file(std::string file_path,
settings.heuristics_only = heuristics_only;
settings.num_cpu_threads = num_cpu_threads;
settings.log_to_console = log_to_console;
// settings.tolerances.relative_tolerance = 1e-10;
// settings.tolerances.absolute_tolerance = 1e-6;
cuopt::linear_programming::benchmark_info_t benchmark_info;
settings.benchmark_info_ptr = &benchmark_info;
auto start_run_solver = std::chrono::high_resolution_clock::now();
Expand Down
6 changes: 6 additions & 0 deletions cpp/src/dual_simplex/branch_and_bound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,12 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
const i_t num_fractional =
fractional_variables(settings, root_relax_soln.x, var_types, fractional);
const f_t root_objective = compute_objective(original_lp, root_relax_soln.x);
if (settings.solution_callback != nullptr) {
std::vector<f_t> original_x;
uncrush_primal_solution(original_problem, original_lp, root_relax_soln.x, original_x);
settings.set_simplex_solution_callback(original_x,
compute_user_objective(original_lp, root_objective));
}
global_variables::mutex_lower.lock();
f_t lower_bound = global_variables::lower_bound = root_objective;
global_variables::mutex_lower.unlock();
Expand Down
1 change: 1 addition & 0 deletions cpp/src/dual_simplex/simplex_solver_settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ struct simplex_solver_settings_t {
i_t inside_mip; // 0 if outside MIP, 1 if inside MIP at root node, 2 if inside MIP at leaf node
std::function<void(std::vector<f_t>&, f_t)> solution_callback;
std::function<void()> heuristic_preemption_callback;
std::function<void(std::vector<f_t>&, f_t)> set_simplex_solution_callback;
mutable logger_t log;
std::atomic<i_t>* concurrent_halt; // if nullptr ignored, if !nullptr, 0 if solver should
// continue, 1 if solver should halt
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/mip/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ list(PREPEND
${CMAKE_CURRENT_SOURCE_DIR}/solver.cu
${CMAKE_CURRENT_SOURCE_DIR}/solver_settings.cu
${CMAKE_CURRENT_SOURCE_DIR}/solver_solution.cu
${CMAKE_CURRENT_SOURCE_DIR}/diversity/assignment_hash_map.cu
${CMAKE_CURRENT_SOURCE_DIR}/diversity/diversity_manager.cu
${CMAKE_CURRENT_SOURCE_DIR}/diversity/multi_armed_bandit.cu
${CMAKE_CURRENT_SOURCE_DIR}/diversity/population.cu
${CMAKE_CURRENT_SOURCE_DIR}/relaxed_lp/relaxed_lp.cu
${CMAKE_CURRENT_SOURCE_DIR}/local_search/local_search.cu
Expand Down
172 changes: 172 additions & 0 deletions cpp/src/mip/diversity/assignment_hash_map.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2024-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.
*/

#include "assignment_hash_map.cuh"

#include <mip/mip_constants.hpp>
#include <utilities/copy_helpers.hpp>

#include <thrust/gather.h>
#include <cub/cub.cuh>
#include <cuda/std/functional>

namespace cuopt {
namespace linear_programming {
namespace detail {

struct combine_hash {
DI size_t operator()(size_t hash_1, size_t hash_2)
{
const std::size_t magic_constant = 0x9e3779b97f4a7c15;
hash_1 ^= hash_2 + magic_constant + (hash_1 << 12) + (hash_1 >> 4);
return hash_1;
}
};

template <typename i_t, typename f_t, int TPB>
__global__ void hash_solution_kernel(raft::device_span<size_t> assignment,
raft::device_span<size_t> reduction_buffer)
{
typedef cub::BlockReduce<size_t, TPB> BlockReduce;
__shared__ typename BlockReduce::TempStorage temp_storage;

size_t th_hash = assignment.size();
#pragma unroll
for (i_t idx = blockIdx.x * blockDim.x + threadIdx.x; idx < assignment.size();
idx += blockDim.x * gridDim.x) {
th_hash = combine_hash()(th_hash, assignment[idx]);
}
size_t hash_sum = BlockReduce(temp_storage).Reduce(th_hash, combine_hash(), TPB);
if (threadIdx.x == 0) { reduction_buffer[blockIdx.x] = hash_sum; }
}

template <typename i_t, typename f_t, int TPB>
__global__ void reduce_hash_kernel(raft::device_span<size_t> reduction_buffer,
size_t* global_hash_sum)
{
typedef cub::BlockReduce<size_t, TPB> BlockReduce;
__shared__ typename BlockReduce::TempStorage temp_storage;
size_t th_hash = reduction_buffer[threadIdx.x];
size_t hash_sum = BlockReduce(temp_storage).Reduce(th_hash, combine_hash(), TPB);
if (threadIdx.x == 0) { *global_hash_sum = hash_sum; }
}

template <typename i_t, typename f_t>
assignment_hash_map_t<i_t, f_t>::assignment_hash_map_t(const problem_t<i_t, f_t>& problem)
: reduction_buffer(problem.n_variables, problem.handle_ptr->get_stream()),
integer_assignment(problem.n_integer_vars, problem.handle_ptr->get_stream()),
hash_sum(problem.handle_ptr->get_stream()),
temp_storage(0, problem.handle_ptr->get_stream())
{
}

// we might move this to be a solution member if it is needed
// currently having an ordered integer array is only needed here
template <typename i_t, typename f_t>
void assignment_hash_map_t<i_t, f_t>::fill_integer_assignment(solution_t<i_t, f_t>& solution)
{
static_assert(sizeof(f_t) == sizeof(size_t), "f_t must be double precision");
thrust::gather(solution.handle_ptr->get_thrust_policy(),
solution.problem_ptr->integer_indices.begin(),
solution.problem_ptr->integer_indices.end(),
solution.assignment.begin(),
reinterpret_cast<double*>(integer_assignment.data()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seriously doubt we will ever use anything but FP64, but in the rare case we instantiate the solver with f_t=FP32, this is going to break/behave badly. We might want to add a static_assert to make sure sizeof(f_t) == sizeof(size_t) in order not to cause issues if users decide to try out FP32

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay let me add the static assert here.

}

template <typename i_t, typename f_t>
size_t assignment_hash_map_t<i_t, f_t>::hash_solution(solution_t<i_t, f_t>& solution)
{
const int TPB = 1024;
fill_integer_assignment(solution);
thrust::fill(
solution.handle_ptr->get_thrust_policy(), reduction_buffer.begin(), reduction_buffer.end(), 0);
hash_solution_kernel<i_t, f_t, TPB>
<<<(integer_assignment.size() + TPB - 1) / TPB, TPB, 0, solution.handle_ptr->get_stream()>>>(
cuopt::make_span(integer_assignment), cuopt::make_span(reduction_buffer));
RAFT_CHECK_CUDA(handle_ptr->get_stream());
// Get the number of blocks used in the hash_solution_kernel
int num_blocks = (integer_assignment.size() + TPB - 1) / TPB;

// If we have more than one block, perform a device-wide reduction using CUB
if (num_blocks > 1) {
// Determine temporary device storage requirements
void* d_temp_storage = nullptr;
size_t temp_storage_bytes = 0;
cub::DeviceReduce::Reduce(d_temp_storage,
temp_storage_bytes,
reduction_buffer.data(),
hash_sum.data(),
num_blocks,
combine_hash(),
0,
solution.handle_ptr->get_stream());

// Allocate temporary storage
temp_storage.resize(temp_storage_bytes, solution.handle_ptr->get_stream());
d_temp_storage = temp_storage.data();

// Run reduction
cub::DeviceReduce::Reduce(d_temp_storage,
temp_storage_bytes,
reduction_buffer.data(),
hash_sum.data(),
num_blocks,
combine_hash(),
0,
solution.handle_ptr->get_stream());

// Return early since we've already computed the hash sum
return hash_sum.value(solution.handle_ptr->get_stream());
} else {
return reduction_buffer.element(0, solution.handle_ptr->get_stream());
}
}

template <typename i_t, typename f_t>
void assignment_hash_map_t<i_t, f_t>::insert(solution_t<i_t, f_t>& solution)
{
size_t sol_hash = hash_solution(solution);
solution_hash_count[sol_hash]++;
}

template <typename i_t, typename f_t>
bool assignment_hash_map_t<i_t, f_t>::check_skip_solution(solution_t<i_t, f_t>& solution,
i_t max_occurance)
{
size_t hash = hash_solution(solution);
if (solution_hash_count[hash] > max_occurance) {
CUOPT_LOG_DEBUG("Skipping solution which is encountered %d times", solution_hash_count[hash]);
return true;
}
return false;
}

#if !MIP_INSTANTIATE_FLOAT && !MIP_INSTANTIATE_DOUBLE
static_assert(false, "MIP_INSTANTIATE_FLOAT or MIP_INSTANTIATE_DOUBLE must be defined");
#endif
Comment on lines +158 to +160
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those atrocious linking errors :) I wish there was a way to throw an error at preprocessor/link time without having to add explicit checks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is the second time i spend significant amount of time to these issues.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just remove FLOAT? do we anticipate using it for MIP?


#if MIP_INSTANTIATE_FLOAT
template class assignment_hash_map_t<int, float>;
#endif

#if MIP_INSTANTIATE_DOUBLE
template class assignment_hash_map_t<int, double>;
#endif

} // namespace detail
} // namespace linear_programming
} // namespace cuopt
45 changes: 45 additions & 0 deletions cpp/src/mip/diversity/assignment_hash_map.cuh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2024-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 <mip/solution/solution.cuh>

namespace cuopt {
namespace linear_programming {
namespace detail {

template <typename i_t, typename f_t>
class assignment_hash_map_t {
public:
assignment_hash_map_t(const problem_t<i_t, f_t>& problem);
void fill_integer_assignment(solution_t<i_t, f_t>& solution);
size_t hash_solution(solution_t<i_t, f_t>& solution);
void insert(solution_t<i_t, f_t>& solution);
bool check_skip_solution(solution_t<i_t, f_t>& solution, i_t max_occurance);

// keep the hash to encounter count of solution hash
std::unordered_map<size_t, i_t> solution_hash_count;
rmm::device_uvector<size_t> reduction_buffer;
rmm::device_uvector<size_t> integer_assignment;
rmm::device_scalar<size_t> hash_sum;
rmm::device_buffer temp_storage;
};

} // namespace detail
} // namespace linear_programming
} // namespace cuopt
Loading