Skip to content

Commit a0118c4

Browse files
Merge pull request #95 from rgsl888prabhu/branch-25.08-merge-branch-25.05
Branch 25.08 merge branch 25.05
2 parents 716502c + 224d694 commit a0118c4

File tree

15 files changed

+32362
-59
lines changed

15 files changed

+32362
-59
lines changed

.github/workflows/nightly.yaml

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,35 @@ jobs:
2323
export DATE=$(date +%F)
2424
export SHA=$(gh api -q '.commit.sha' "repos/nvidia/cuopt/branches/${CUOPT_BRANCH}")
2525
26-
RUN_ID=$(gh workflow run build.yaml \
27-
-f branch=${CUOPT_BRANCH} \
28-
-f sha=${SHA} \
29-
-f date=${DATE} \
30-
-f build_type=nightly \
31-
--json databaseId --jq '.databaseId')
26+
gh workflow run build.yaml \
27+
-f branch="${CUOPT_BRANCH}" \
28+
-f sha="${SHA}" \
29+
-f date="${DATE}" \
30+
-f build_type=nightly
31+
32+
# Wait a short bit for the workflow to register (optional)
33+
sleep 3
34+
35+
# Get the latest run ID for this workflow on this branch
36+
RUN_ID=$(gh run list --workflow=build.yaml --branch="${CUOPT_BRANCH}" --json databaseId --limit 1 | jq -r '.[0].databaseId')
37+
38+
STATUS=$(gh run view $RUN_ID --json status,conclusion --jq '.status')
39+
CONCLUSION=$(gh run view $RUN_ID --json status,conclusion --jq '.conclusion')
40+
41+
while [[ "$STATUS" != "completed" || "$CONCLUSION" == "null" ]]; do
42+
echo "Status: $STATUS, Conclusion: $CONCLUSION — waiting 10 seconds..."
43+
sleep 10
44+
STATUS=$(gh run view $RUN_ID --json status,conclusion --jq '.status')
45+
CONCLUSION=$(gh run view $RUN_ID --json status,conclusion --jq '.conclusion')
46+
done
47+
48+
echo "Workflow run finished with conclusion: $CONCLUSION"
49+
50+
if [[ "$CONCLUSION" != "success" ]]; then
51+
echo "Build did not succeed"
52+
exit 1
53+
fi
3254
33-
# Wait for workflow to complete
34-
gh run watch $RUN_ID
3555
3656
trigger-test:
3757
runs-on: ubuntu-latest

.github/workflows/test.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
secrets: inherit
3232
uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@branch-25.06
3333
with:
34+
run_codecov: false
3435
build_type: ${{ inputs.build_type }}
3536
branch: ${{ inputs.branch }}
3637
date: ${{ inputs.date }}

CONTRIBUTING.md

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,10 @@ source and contribute to its development. Other operating systems may be compati
6464
currently tested.
6565

6666
Building NVIDIA cuOpt with the provided conda environment is recommended for users who wish to enable all
67-
library features. The following instructions are for building with a conda environment. Dependencies
68-
for a minimal build of NVIDIA cuOpt without using conda are also listed below.
67+
library features. The following instructions are for building with a conda environment.
6968

7069
### General requirements
7170

72-
Compilers:
73-
74-
These will be installed while creating the Conda environment
75-
76-
* `gcc` version 13.0+
77-
* `nvcc` version 12.8+
78-
* `cmake` version 3.30.4+
79-
8071
CUDA/GPU Runtime:
8172

8273
* CUDA 12.8
@@ -107,11 +98,13 @@ cd $CUOPT_HOME
10798

10899
#### Building with a conda environment
109100

110-
**Note:** Using a conda environment is the easiest way to satisfy the library's dependencies.
101+
**Note:** Building from source without conda is very difficult. We highly recommend that users build cuOpt inside a conda environment
111102

112103
- Create the conda development environment:
113104

114-
Please install conda if you don't have it already. You can install it from [https://docs.conda.io/en/latest/miniconda.html](https://docs.conda.io/en/latest/miniconda.html)
105+
Please install conda if you don't have it already. You can install [miniforge](https://conda-forge.org/download/) or [miniconda](https://www.anaconda.com/docs/getting-started/miniconda/install#linux)
106+
107+
**Note:** We recommend using [mamba](https://mamba.readthedocs.io/en/latest/installation/mamba-installation.html) as the package manager for the conda environment. Mamba is faster and more efficient than conda. And it's the default package manager for miniforge. If you are using mamba just replace `conda` with `mamba` in the following commands.
115108

116109
```bash
117110
# create the conda environment (assuming in base `cuopt` directory)
@@ -164,8 +157,8 @@ To run the C++ tests, run
164157

165158
```bash
166159
cd $CUOPT_HOME/datasets && get_test_data.sh
167-
cd $CUOPT_HOME/datasets/linear_programming && download_pdlp_test_dataset.sh
168-
cd $CUOPT_HOME/datasets/mip && download_miplib_test_dataset.sh
160+
cd $CUOPT_HOME && datasets/linear_programming/download_pdlp_test_dataset.sh
161+
datasets/mip/download_miplib_test_dataset.sh
169162
export RAPIDS_DATASET_ROOT_DIR=$CUOPT_HOME/datasets/
170163
ctest --test-dir ${CUOPT_HOME}/cpp/build # libcuopt
171164
```
@@ -176,8 +169,8 @@ To run python tests, run
176169
```bash
177170

178171
cd $CUOPT_HOME/datasets && get_test_data.sh
179-
cd $CUOPT_HOME/datasets/linear_programming && download_pdlp_test_dataset.sh
180-
cd $CUOPT_HOME/datasets/mip && download_miplib_test_dataset.sh
172+
cd $CUOPT_HOME && datasets/linear_programming/download_pdlp_test_dataset.sh
173+
datasets/mip/download_miplib_test_dataset.sh
181174
export RAPIDS_DATASET_ROOT_DIR=$CUOPT_HOME/datasets/
182175
cd $CUOPT_HOME/python
183176
pytest -v ${CUOPT_HOME}/python/cuopt/cuopt/tests

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ cuOpt supports the following APIs:
2626
- Linear Programming (LP)
2727
- Mixed Integer Linear Programming (MILP)
2828
- Routing (TSP, VRP, and PDP)
29+
30+
This repo is also hosted as a [COIN-OR](http://github.com/coin-or/cuopt/) project.
2931

3032
## Installation
3133

cpp/src/mip/diversity/diversity_manager.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ solution_t<i_t, f_t> diversity_manager_t<i_t, f_t>::run_solver()
351351
ls.lp_optimal_exists = true;
352352
if (lp_result.get_termination_status() == pdlp_termination_status_t::Optimal) {
353353
// get lp user objective and pass it to set_new_user_bound
354-
set_new_user_bound(problem_ptr->get_user_obj_from_solver_obj(lp_result.get_objective_value()));
354+
set_new_user_bound(lp_result.get_objective_value());
355355
} else if (lp_result.get_termination_status() == pdlp_termination_status_t::PrimalInfeasible) {
356356
// PDLP's infeasibility detection isn't an exact method and might be subject to false positives.
357357
// Issue a warning, and continue solving.

cpp/src/mip/presolve/bounds_presolve.cu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ void bound_presolve_t<i_t, f_t>::calc_and_set_updated_constraint_bounds(problem_
359359
min_act = make_span(upd.min_activity),
360360
max_act = make_span(upd.max_activity),
361361
cnst_lb = make_span(pb.constraint_lower_bounds),
362-
cnst_ub = make_span(pb.constraint_upper_bounds)] __device__(i_t idx) -> i_t {
362+
cnst_ub = make_span(pb.constraint_upper_bounds)] __device__(i_t idx) {
363363
auto min_a = min_act[idx];
364364
auto max_a = max_act[idx];
365365
auto c_lb = cnst_lb[idx];

cpp/src/mip/problem/problem.cu

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,23 +388,25 @@ void problem_t<i_t, f_t>::check_problem_representation(bool check_transposed,
388388
"Sizes for vectors related to the constraints are not the same.");
389389

390390
// Check the validity of bounds
391-
cuopt_assert(
391+
cuopt_expects(
392392
thrust::all_of(handle_ptr->get_thrust_policy(),
393393
thrust::make_counting_iterator<i_t>(0),
394394
thrust::make_counting_iterator<i_t>(n_variables),
395395
[variable_lower_bounds = variable_lower_bounds.data(),
396396
variable_upper_bounds = variable_upper_bounds.data()] __device__(i_t idx) {
397397
return variable_lower_bounds[idx] <= variable_upper_bounds[idx];
398398
}),
399+
error_type_t::ValidationError,
399400
"Variable bounds are invalid");
400-
cuopt_assert(
401+
cuopt_expects(
401402
thrust::all_of(handle_ptr->get_thrust_policy(),
402403
thrust::make_counting_iterator<i_t>(0),
403404
thrust::make_counting_iterator<i_t>(n_constraints),
404405
[constraint_lower_bounds = constraint_lower_bounds.data(),
405406
constraint_upper_bounds = constraint_upper_bounds.data()] __device__(i_t idx) {
406407
return constraint_lower_bounds[idx] <= constraint_upper_bounds[idx];
407408
}),
409+
error_type_t::ValidationError,
408410
"Constraints bounds are invalid");
409411

410412
if (check_mip_related_data) {

cpp/tests/mip/termination_test.cu

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@
4545

4646
namespace cuopt::linear_programming::test {
4747

48-
static std::pair<mip_termination_status_t, double> test_mps_file(std::string test_instance,
49-
bool heuristics_only = true,
50-
double time_limit = 10)
48+
static std::tuple<mip_termination_status_t, double, double> test_mps_file(
49+
std::string test_instance, bool heuristics_only = true, double time_limit = 10)
5150
{
5251
const raft::handle_t handle_{};
5352

@@ -59,58 +58,69 @@ static std::pair<mip_termination_status_t, double> test_mps_file(std::string tes
5958
settings.time_limit = time_limit;
6059
settings.heuristics_only = heuristics_only;
6160
mip_solution_t<int, double> solution = solve_mip(&handle_, problem, settings);
62-
return std::make_pair(solution.get_termination_status(), solution.get_objective_value());
61+
return std::make_tuple(solution.get_termination_status(),
62+
solution.get_objective_value(),
63+
solution.get_solution_bound());
6364
}
6465

6566
TEST(termination_status, trivial_presolve_optimality_test)
6667
{
67-
auto [termination_status, obj_val] = test_mps_file("mip/trivial-presolve-optimality.mps");
68+
auto [termination_status, obj_val, lb] = test_mps_file("mip/trivial-presolve-optimality.mps");
6869
EXPECT_EQ(termination_status, mip_termination_status_t::Optimal);
6970
EXPECT_EQ(obj_val, -1);
7071
}
7172

7273
TEST(termination_status, presolve_optimality_test)
7374
{
74-
auto [termination_status, obj_val] = test_mps_file("mip/sudoku.mps");
75+
auto [termination_status, obj_val, lb] = test_mps_file("mip/sudoku.mps");
7576
EXPECT_EQ(termination_status, mip_termination_status_t::Optimal);
7677
EXPECT_EQ(obj_val, 0);
7778
}
7879

7980
TEST(termination_status, presolve_infeasible_test)
8081
{
81-
auto [termination_status, obj_val] = test_mps_file("mip/presolve-infeasible.mps");
82+
auto [termination_status, obj_val, lb] = test_mps_file("mip/presolve-infeasible.mps");
8283
EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible);
8384
}
8485

8586
TEST(termination_status, feasible_found_test)
8687
{
87-
auto [termination_status, obj_val] = test_mps_file("mip/gen-ip054.mps");
88+
auto [termination_status, obj_val, lb] = test_mps_file("mip/gen-ip054.mps");
8889
EXPECT_EQ(termination_status, mip_termination_status_t::FeasibleFound);
8990
}
9091

9192
TEST(termination_status, timeout_test)
9293
{
93-
auto [termination_status, obj_val] = test_mps_file("mip/stein9inf.mps");
94+
auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps");
9495
EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit);
9596
}
9697

9798
TEST(termination_status, optimality_test)
9899
{
99-
auto [termination_status, obj_val] = test_mps_file("mip/bb_optimality.mps", false);
100+
auto [termination_status, obj_val, lb] = test_mps_file("mip/bb_optimality.mps", false);
100101
EXPECT_EQ(termination_status, mip_termination_status_t::Optimal);
101102
EXPECT_EQ(obj_val, 2);
102103
}
103104

105+
// Ensure the lower bound on maximization problems when BB times out has the right sign
106+
TEST(termination_status, lower_bound_bb_timeout)
107+
{
108+
auto [termination_status, obj_val, lb] = test_mps_file("mip/cod105_max.mps", false, 0.5);
109+
EXPECT_EQ(termination_status, mip_termination_status_t::FeasibleFound);
110+
EXPECT_EQ(obj_val, 12);
111+
EXPECT_GE(lb, obj_val);
112+
}
113+
104114
TEST(termination_status, bb_infeasible_test)
105115
{
106116
// First, check that presolve doesn't reduce the problem to infeasibility
107117
{
108-
auto [termination_status, obj_val] = test_mps_file("mip/stein9inf.mps", true, 1);
118+
auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", true, 1);
109119
EXPECT_EQ(termination_status, mip_termination_status_t::TimeLimit);
110120
}
111121
// Ensure that B&B proves the MIP infeasible
112122
{
113-
auto [termination_status, obj_val] = test_mps_file("mip/stein9inf.mps", false, 30);
123+
auto [termination_status, obj_val, lb] = test_mps_file("mip/stein9inf.mps", false, 30);
114124
EXPECT_EQ(termination_status, mip_termination_status_t::Infeasible);
115125
}
116126
}

cpp/tests/mip/unit_test.cu

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,11 @@ INSTANTIATE_TEST_SUITE_P(
215215
MILPTests,
216216
MILPTestParams,
217217
testing::Values(
218-
std::make_tuple(
219-
true, true, true, cuopt::linear_programming::mip_termination_status_t::FeasibleFound),
218+
std::make_tuple(true, true, true, cuopt::linear_programming::mip_termination_status_t::Optimal),
220219
std::make_tuple(
221220
false, true, false, cuopt::linear_programming::mip_termination_status_t::Optimal),
222221
std::make_tuple(
223-
true, false, true, cuopt::linear_programming::mip_termination_status_t::FeasibleFound),
222+
true, false, true, cuopt::linear_programming::mip_termination_status_t::Optimal),
224223
std::make_tuple(
225224
false, false, false, cuopt::linear_programming::mip_termination_status_t::Optimal)));
226225

0 commit comments

Comments
 (0)