Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
943b632
add c api callbacks
akifcorduk Jan 15, 2026
41a3b68
add user context to the callbacks
akifcorduk Jan 16, 2026
1e7a2e5
handle ai reviews
akifcorduk Jan 16, 2026
fda3f4f
add doxygen string
akifcorduk Jan 16, 2026
4e50db4
python callback casting issues
akifcorduk Jan 16, 2026
826b3c0
more review comments
akifcorduk Jan 16, 2026
860e060
address review comments
akifcorduk Jan 26, 2026
b28aba2
add presolve data to callbacks and do cleanup of presolve data. Callb…
akifcorduk Jan 26, 2026
5d328b4
add papilo presolve data, enable callbacks with presolve, change devi…
akifcorduk Jan 27, 2026
5b6c3d4
Merge branch 'release/26.02' of github.com:NVIDIA/cuopt into c_api_ca…
akifcorduk Jan 27, 2026
c666c32
add substituion merge changes
akifcorduk Jan 27, 2026
f57f782
fix merge conflicts
akifcorduk Jan 27, 2026
21f687c
handle ai reviews
akifcorduk Jan 27, 2026
0166f23
attempt at fixing imcumbent tests
akifcorduk Jan 27, 2026
6350e4a
remove papilo crush, disable presolve when set_solution callback give…
akifcorduk Jan 28, 2026
f5b5c3e
add user/dual bound
akifcorduk Jan 28, 2026
4c813c7
handle ai reviews
akifcorduk Jan 28, 2026
2d92a0a
fix python size issue
akifcorduk Jan 28, 2026
81e477e
more frequent external queue updates
aliceb-nv Jan 21, 2026
6c35c4d
add some comments on external solutions and fix b&B logs
akifcorduk Jan 28, 2026
a8776a2
fix server test and add comments
akifcorduk Jan 29, 2026
203ce1c
sort csr and improve server tests so it is not fully reduced at preso…
akifcorduk Jan 29, 2026
ab7029c
Merge branch 'release/26.02' of github.com:NVIDIA/cuopt into c_api_ca…
akifcorduk Jan 29, 2026
f93dcfc
attempt at fixing race conditions
akifcorduk Jan 30, 2026
0547eaf
change the max compressed size
akifcorduk Jan 30, 2026
7094cd0
move sort out
akifcorduk Jan 30, 2026
f770594
remove faulty sync stream
akifcorduk Jan 30, 2026
0000549
add prints and lineinfo the build
akifcorduk Jan 30, 2026
d5b28a5
test not adding solutions in fj
akifcorduk Feb 2, 2026
d68957f
reenable add to population
akifcorduk Feb 2, 2026
cd87d5c
attempt at fixing the race condition on rins
akifcorduk Feb 2, 2026
c6dfaee
another attempt at fixing race condition
akifcorduk Feb 2, 2026
5084a08
move sort inside presolve
akifcorduk Feb 2, 2026
e6b6642
fix incorrect problem copy handle logic
aliceb-nv Feb 2, 2026
02199cb
fix callback setupts and logs
akifcorduk Feb 3, 2026
74e7515
Merge branch 'release/26.02' of github.com:NVIDIA/cuopt into c_api_ca…
akifcorduk Feb 3, 2026
4cac4f7
fix merge issue
akifcorduk Feb 3, 2026
41a9592
fix settings handle in new apis
akifcorduk Feb 3, 2026
1600789
disable scaling and add papilo uncrush
akifcorduk Feb 3, 2026
6023885
add uncrsuh
akifcorduk Feb 3, 2026
fc3be68
do one final incumbent check
akifcorduk Feb 3, 2026
3c2d55d
incumbent check at finally
akifcorduk Feb 3, 2026
1f2523a
fix presolve data early return
akifcorduk Feb 4, 2026
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
4 changes: 2 additions & 2 deletions ci/run_cuopt_pytests.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail
Expand All @@ -9,4 +9,4 @@ set -euo pipefail
# Support invoking run_cuopt_pytests.sh outside the script directory
cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuopt/cuopt/

pytest --cache-clear "$@" tests
pytest -s --cache-clear "$@" tests
4 changes: 2 additions & 2 deletions ci/run_cuopt_server_pytests.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail
Expand All @@ -9,4 +9,4 @@ set -euo pipefail
# Support invoking run_cuopt_server_pytests.sh outside the script directory
cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuopt_server/cuopt_server/

pytest --cache-clear "$@" tests
pytest -s --cache-clear "$@" tests
2 changes: 1 addition & 1 deletion conda/recipes/libcuopt/recipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ cache:
- AWS_SESSION_TOKEN
env:
# Enable assertions (-a flag) for PR builds, but not for nightly or release builds
BUILD_EXTRA_FLAGS: '${{ "-a" if build_type == "pull-request" else "" }}'
BUILD_EXTRA_FLAGS: '${{ "-a --host-lineinfo" if build_type == "pull-request" else "" }}'
CMAKE_C_COMPILER_LAUNCHER: ${{ env.get("CMAKE_C_COMPILER_LAUNCHER") }}
CMAKE_CUDA_COMPILER_LAUNCHER: ${{ env.get("CMAKE_CUDA_COMPILER_LAUNCHER") }}
CMAKE_CXX_COMPILER_LAUNCHER: ${{ env.get("CMAKE_CXX_COMPILER_LAUNCHER") }}
Expand Down
70 changes: 70 additions & 0 deletions cpp/include/cuopt/linear_programming/cuopt_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,76 @@ cuopt_int_t cuOptGetFloatParameter(cuOptSolverSettings settings,
const char* parameter_name,
cuopt_float_t* parameter_value);

/**
* @brief Type of callback for receiving incumbent MIP solutions with user context.
*
* @param[in] solution - Pointer to incumbent solution values.
* The allocated array for solution pointer must be at least the number of variables in the original
* problem.
* @param[in] objective_value - Pointer to incumbent objective value.
* @param[in] solution_bound - Pointer to current solution (dual/user) bound.
* @param[in] user_data - Pointer to user data.
* @note All pointer arguments (solution, objective_value, solution_bound, user_data) refer to host
* memory and are only valid during the callback invocation. Do not pass device/GPU pointers.
* Copy any data you need to keep after the callback returns.
*/
typedef void (*cuOptMIPGetSolutionCallback)(const cuopt_float_t* solution,
const cuopt_float_t* objective_value,
const cuopt_float_t* solution_bound,
void* user_data);

/**
* @brief Type of callback for injecting MIP solutions with user context.
*
* @param[out] solution - Pointer to solution values to set.
* The allocated array for solution pointer must be at least the number of variables in the original
* problem.
* @param[out] objective_value - Pointer to objective value to set.
* @param[in] solution_bound - Pointer to current solution (dual/user) bound.
* @param[in] user_data - Pointer to user data.
* @note All pointer arguments (solution, objective_value, solution_bound, user_data) refer to host
* memory and are only valid during the callback invocation. Do not pass device/GPU pointers.
* Copy any data you need to keep after the callback returns.
*/
typedef void (*cuOptMIPSetSolutionCallback)(cuopt_float_t* solution,
cuopt_float_t* objective_value,
const cuopt_float_t* solution_bound,
void* user_data);

/**
* @brief Register a callback to receive incumbent MIP solutions.
*
* @param[in] settings - The solver settings object.
* @param[in] callback - Callback function to receive incumbent solutions.
* @param[in] user_data - User-defined pointer passed through to the callback.
* It will be forwarded to ``cuOptMIPGetSolutionCallback`` when invoked.
* @note The callback arguments refer to host memory and are only valid during the callback
* invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback
* returns.
*
* @return A status code indicating success or failure.
*/
cuopt_int_t cuOptSetMIPGetSolutionCallback(cuOptSolverSettings settings,
cuOptMIPGetSolutionCallback callback,
void* user_data);

/**
* @brief Register a callback to inject MIP solutions.
*
* @param[in] settings - The solver settings object.
* @param[in] callback - Callback function to inject solutions.
* @param[in] user_data - User-defined pointer passed through to the callback.
* It will be forwarded to ``cuOptMIPSetSolutionCallback`` when invoked.
* @note Registering a set-solution callback disables presolve.
* @note The callback arguments refer to host memory and are only valid during the callback
* invocation. Do not pass device/GPU pointers. Copy any data you need to keep after the callback
* returns.
*
* @return A status code indicating success or failure.
*/
cuopt_int_t cuOptSetMIPSetSolutionCallback(cuOptSolverSettings settings,
cuOptMIPSetSolutionCallback callback,
void* user_data);
/**
* @brief Set the initial primal solution for an LP solve.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ class mip_solver_settings_t {

/**
* @brief Set the callback for the user solution
*
* @param[in] callback - Callback handler for user solutions.
* @param[in] user_data - Pointer to user-defined data forwarded to the callback.
*/
void set_mip_callback(internals::base_solution_callback_t* callback = nullptr);
void set_mip_callback(internals::base_solution_callback_t* callback = nullptr,
void* user_data = nullptr);

/**
* @brief Add an primal solution.
Expand Down Expand Up @@ -91,7 +95,7 @@ class mip_solver_settings_t {

/** Initial primal solutions */
std::vector<std::shared_ptr<rmm::device_uvector<f_t>>> initial_solutions;
bool mip_scaling = true;
bool mip_scaling = false;
bool presolve = true;
// this is for extracting info from different places of the solver during
// benchmarks
Expand Down
32 changes: 28 additions & 4 deletions cpp/include/cuopt/linear_programming/mip/solver_stats.hpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */
#pragma once

#include <atomic>
#include <limits>
namespace cuopt::linear_programming {

template <typename i_t, typename f_t>
struct solver_stats_t {
f_t total_solve_time = 0.;
f_t presolve_time = 0.;
f_t solution_bound = std::numeric_limits<f_t>::min();
// Direction-neutral placeholder; solver_context initializes based on maximize/minimize.
solver_stats_t() : solution_bound(std::numeric_limits<f_t>::infinity()) {}

solver_stats_t(const solver_stats_t& other) { *this = other; }

solver_stats_t& operator=(const solver_stats_t& other)
{
if (this == &other) { return *this; }
total_solve_time = other.total_solve_time;
presolve_time = other.presolve_time;
solution_bound.store(other.solution_bound.load(std::memory_order_relaxed),
std::memory_order_relaxed);
num_nodes = other.num_nodes;
num_simplex_iterations = other.num_simplex_iterations;
return *this;
}

f_t get_solution_bound() const { return solution_bound.load(std::memory_order_relaxed); }

void set_solution_bound(f_t value) { solution_bound.store(value, std::memory_order_relaxed); }

f_t total_solve_time = 0.;
f_t presolve_time = 0.;
std::atomic<f_t> solution_bound;
i_t num_nodes = 0;
i_t num_simplex_iterations = 0;
Comment on lines +36 to 40
Copy link

@coderabbitai coderabbitai bot Jan 28, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check f_t typedef definition and how solver_stats_t is used
rg -n "using f_t|typedef.*f_t|#define f_t" --type=cpp --type=hpp | head -20

Repository: NVIDIA/cuopt

Length of output: 83


🏁 Script executed:

# Check the full context of solver_stats.hpp to see if there's documentation
cat -n cpp/include/cuopt/linear_programming/mip/solver_stats.hpp

Repository: NVIDIA/cuopt

Length of output: 1791


🏁 Script executed:

# Search for migration guides, release notes, or breaking change documentation
fd -e md -e rst | xargs rg -l "breaking\|migration\|ABI" 2>/dev/null | head -10

Repository: NVIDIA/cuopt

Length of output: 38


🏁 Script executed:

# Check if solution_bound has any Doxygen or documentation comments
rg -B5 "solution_bound" cpp/include/cuopt --type=cpp --type=hpp | head -30

Repository: NVIDIA/cuopt

Length of output: 83


🏁 Script executed:

# Search for f_t typedef/using declarations
rg -n "using f_t|f_t =" cpp/include/cuopt -A2 | head -40

Repository: NVIDIA/cuopt

Length of output: 38


🏁 Script executed:

# Check if there are Doxygen comments in the header file
rg -B3 "struct solver_stats_t|solution_bound" cpp/include/cuopt/linear_programming/mip/solver_stats.hpp

Repository: NVIDIA/cuopt

Length of output: 849


🏁 Script executed:

# Search for any migration guides, docs, or breaking change notices in the repo
fd -type f \( -name "*.md" -o -name "*.rst" -o -name "CHANGELOG*" \) | xargs rg -l "solver_stats\|breaking.*ABI\|migration" 2>/dev/null

Repository: NVIDIA/cuopt

Length of output: 228


🏁 Script executed:

# Check if this struct is used elsewhere to understand access patterns
rg "solver_stats_t" cpp/src cpp/test --type=cpp -A2 | head -50

Repository: NVIDIA/cuopt

Length of output: 104


🏁 Script executed:

# Find where solver_stats_t is instantiated to see what f_t is used as
rg "solver_stats_t<" cpp/include cpp/src --type=cpp -A1 -B1

Repository: NVIDIA/cuopt

Length of output: 1387


🏁 Script executed:

# Check for any documentation or comments in the header about the atomic change
rg -n "solution_bound|atomic" cpp/include/cuopt/linear_programming/mip/solver_stats.hpp -B2

Repository: NVIDIA/cuopt

Length of output: 797


🏁 Script executed:

# Search for any documentation files mentioning this struct
find . -name "*.md" -o -name "*.rst" | xargs grep -l "solver_stats" 2>/dev/null

Repository: NVIDIA/cuopt

Length of output: 40


🏁 Script executed:

# Check if there are any Doxygen-style comments in related headers
cat cpp/include/cuopt/linear_programming/mip/solver_stats.hpp | head -20

Repository: NVIDIA/cuopt

Length of output: 695


Add Doxygen documentation and breaking change notice for public API header.

The std::atomic<f_t> change correctly implements concurrent access with load()/store() using relaxed memory ordering, and the custom copy assignment properly handles the atomic member. However, as a public header in cpp/include/cuopt/**, this breaking change requires:

  • Doxygen documentation comments on the struct and new accessor methods
  • Documentation of thread-safety behavior (specifically that solution_bound is now thread-safe while other members are not)
  • A migration notice or deprecation guide for users of the old non-atomic member
  • Explicit guarantee that std::atomic<f_t> is lock-free on supported platforms

Consider adding:

/// Solver statistics. Note: solution_bound is now atomic for thread-safe concurrent access.
/// Breaking change: struct layout changed. See migration guide in docs/ for upgrading.
template <typename i_t, typename f_t>
struct solver_stats_t {
  // ...
  /// Thread-safe solution bound (updated via set_solution_bound())
  std::atomic<f_t> solution_bound;
🤖 Prompt for AI Agents
In `@cpp/include/cuopt/linear_programming/mip/solver_stats.hpp` around lines 36 -
40, Add Doxygen comments to the public struct solver_stats_t and its new
accessor methods (e.g., set_solution_bound() and get_solution_bound())
describing purpose and usage, include a short thread-safety note stating that
solution_bound is now atomic and safe for concurrent access while other members
are not, add a breaking-change/migration notice in the header comment pointing
to docs/ migration guide for users of the previous non-atomic member, and
explicitly state the expectation about lock-free behavior (that std::atomic<f_t>
should be lock-free on supported platforms) in the documentation block so users
are aware of the change and its platform implications.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't officially support c++ api, so it is an intermediate class which is converted to public apis which are C and python.

Copy link

Choose a reason for hiding this comment

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

@akifcorduk, thank you for the clarification! That's helpful context. Since this is an internal/intermediate class and the C++ API is not officially supported, the extensive Doxygen documentation and migration guide suggestions I made are not necessary. The breaking change handling at the C and Python API layers is what matters.

I've noted this for future reviews so I don't flag similar internal C++ classes with the same concerns.


✏️ Learnings added
Learnt from: akifcorduk
Repo: NVIDIA/cuopt PR: 779
File: cpp/include/cuopt/linear_programming/mip/solver_stats.hpp:36-40
Timestamp: 2026-01-28T15:32:45.040Z
Learning: C++ headers under cpp/include/cuopt are internal/intermediate infrastructure, not public-facing. The officially supported public APIs for cuOpt are C (via cuopt_c.h) and Python. Documentation and breaking change requirements apply to those public APIs, not to internal C++ classes.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: chris-maes
Repo: NVIDIA/cuopt PR: 602
File: cpp/src/linear_programming/solve.cu:732-742
Timestamp: 2025-12-04T20:09:09.264Z
Learning: In cpp/src/linear_programming/solve.cu, the barrier solver does not currently return INFEASIBLE or UNBOUNDED status. It only returns OPTIMAL, TIME_LIMIT, NUMERICAL_ISSUES, or CONCURRENT_LIMIT.

Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Validate correct initialization of variable bounds, constraint coefficients, and algorithm state before solving; ensure reset when transitioning between algorithm phases (presolve, simplex, diving, crossover)

Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Refactor code duplication in solver components (3+ occurrences) into shared utilities; for GPU kernels, use templated device functions to avoid duplication

Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Validate algorithm correctness in optimization logic: simplex pivots, branch-and-bound decisions, routing heuristics, and constraint/objective handling must produce correct results

Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cpp,hpp,h} : Prevent thread-unsafe use of global and static variables; use proper mutex/synchronization in server code accessing shared solver state

Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{h,hpp,py} : Verify C API does not break ABI stability (no struct layout changes, field reordering); maintain backward compatibility in Python and server APIs with deprecation warnings

Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to docs/**/*.{rst,md} : Document numerical tolerances, parameter ranges, and algorithm phase descriptions in API documentation when solver behavior or tolerances change

Learnt from: aliceb-nv
Repo: NVIDIA/cuopt PR: 798
File: cpp/src/utilities/models/fj_predictor/quantize.cpp:1149-1161
Timestamp: 2026-01-26T14:17:25.105Z
Learning: The files in cpp/src/utilities/models/*/quantize.cpp (fj_predictor, cpufj_predictor, pdlp_predictor, dualsimplex_predictor, bounds_strengthening_predictor) are autogenerated model artifacts and should not have manual code changes suggested; any improvements should target the model generation tooling or calling code validation layer.

Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Ensure race conditions are absent in multi-GPU code and multi-threaded server implementations; verify proper synchronization of shared state

Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Reduce tight coupling between solver components (presolve, simplex, basis, barrier); increase modularity and reusability of optimization algorithms

Learnt from: CR
Repo: NVIDIA/cuopt PR: 0
File: .github/.coderabbit_review_guide.md:0-0
Timestamp: 2025-11-25T10:20:49.822Z
Learning: Applies to **/*.{cu,cuh,cpp,hpp,h} : Ensure variables and constraints are accessed from the correct problem context (original vs presolve vs folded vs postsolve); verify index mapping consistency across problem transformations

};
Expand Down
5 changes: 3 additions & 2 deletions cpp/include/cuopt/linear_programming/solver_settings.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */
Expand Down Expand Up @@ -81,7 +81,8 @@ class solver_settings_t {
void add_initial_mip_solution(const f_t* initial_solution,
i_t size,
rmm::cuda_stream_view stream = rmm::cuda_stream_default);
void set_mip_callback(internals::base_solution_callback_t* callback = nullptr);
void set_mip_callback(internals::base_solution_callback_t* callback = nullptr,
void* user_data = nullptr);

const pdlp_warm_start_data_view_t<i_t, f_t>& get_pdlp_warm_start_data_view() const noexcept;
const std::vector<internals::base_solution_callback_t*> get_mip_callbacks() const;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */
Expand All @@ -17,17 +17,6 @@ namespace internals {

class default_get_solution_callback_t : public get_solution_callback_t {
public:
PyObject* get_numba_matrix(void* data, std::size_t size)
{
PyObject* pycl = (PyObject*)this->pyCallbackClass;

if (isFloat) {
return PyObject_CallMethod(pycl, "get_numba_matrix", "(lls)", data, size, "float32");
} else {
return PyObject_CallMethod(pycl, "get_numba_matrix", "(lls)", data, size, "float64");
}
}

PyObject* get_numpy_array(void* data, std::size_t size)
{
PyObject* pycl = (PyObject*)this->pyCallbackClass;
Expand All @@ -38,33 +27,33 @@ class default_get_solution_callback_t : public get_solution_callback_t {
}
}

void get_solution(void* data, void* objective_value) override
void get_solution(void* data,
void* objective_value,
void* solution_bound,
void* user_data) override
{
PyObject* numba_matrix = get_numba_matrix(data, n_variables);
PyObject* numpy_array = get_numba_matrix(objective_value, 1);
PyObject* res =
PyObject_CallMethod(this->pyCallbackClass, "get_solution", "(OO)", numba_matrix, numpy_array);
Py_DECREF(numba_matrix);
PyObject* numpy_matrix = get_numpy_array(data, n_variables);
PyObject* numpy_array = get_numpy_array(objective_value, 1);
PyObject* numpy_bound = get_numpy_array(solution_bound, 1);
PyObject* py_user_data = user_data == nullptr ? Py_None : static_cast<PyObject*>(user_data);
PyObject* res = PyObject_CallMethod(this->pyCallbackClass,
"get_solution",
"(OOOO)",
numpy_matrix,
numpy_array,
numpy_bound,
py_user_data);
Py_DECREF(numpy_matrix);
Py_DECREF(numpy_array);
Py_DECREF(res);
Py_DECREF(numpy_bound);
if (res != nullptr) { Py_DECREF(res); }
}

PyObject* pyCallbackClass;
};

class default_set_solution_callback_t : public set_solution_callback_t {
public:
PyObject* get_numba_matrix(void* data, std::size_t size)
{
PyObject* pycl = (PyObject*)this->pyCallbackClass;

if (isFloat) {
return PyObject_CallMethod(pycl, "get_numba_matrix", "(lls)", data, size, "float32");
} else {
return PyObject_CallMethod(pycl, "get_numba_matrix", "(lls)", data, size, "float64");
}
}

PyObject* get_numpy_array(void* data, std::size_t size)
{
PyObject* pycl = (PyObject*)this->pyCallbackClass;
Expand All @@ -75,15 +64,26 @@ class default_set_solution_callback_t : public set_solution_callback_t {
}
}

void set_solution(void* data, void* objective_value) override
void set_solution(void* data,
void* objective_value,
void* solution_bound,
void* user_data) override
{
PyObject* numba_matrix = get_numba_matrix(data, n_variables);
PyObject* numpy_array = get_numba_matrix(objective_value, 1);
PyObject* res =
PyObject_CallMethod(this->pyCallbackClass, "set_solution", "(OO)", numba_matrix, numpy_array);
Py_DECREF(numba_matrix);
PyObject* numpy_matrix = get_numpy_array(data, n_variables);
PyObject* numpy_array = get_numpy_array(objective_value, 1);
PyObject* numpy_bound = get_numpy_array(solution_bound, 1);
PyObject* py_user_data = user_data == nullptr ? Py_None : static_cast<PyObject*>(user_data);
PyObject* res = PyObject_CallMethod(this->pyCallbackClass,
"set_solution",
"(OOOO)",
numpy_matrix,
numpy_array,
numpy_bound,
py_user_data);
Py_DECREF(numpy_matrix);
Py_DECREF(numpy_array);
Py_DECREF(res);
Py_DECREF(numpy_bound);
if (res != nullptr) { Py_DECREF(res); }
}

PyObject* pyCallbackClass;
Expand Down
14 changes: 12 additions & 2 deletions cpp/include/cuopt/linear_programming/utilities/internals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,23 @@ class base_solution_callback_t : public Callback {
this->n_variables = n_variables_;
}

void set_user_data(void* input_user_data) { user_data = input_user_data; }
void* get_user_data() const { return user_data; }

virtual base_solution_callback_type get_type() const = 0;

protected:
bool isFloat = true;
size_t n_variables = 0;
void* user_data = nullptr;
};

class get_solution_callback_t : public base_solution_callback_t {
public:
virtual void get_solution(void* data, void* objective_value) = 0;
virtual void get_solution(void* data,
void* objective_value,
void* solution_bound,
void* user_data) = 0;
base_solution_callback_type get_type() const override
{
return base_solution_callback_type::GET_SOLUTION;
Expand All @@ -49,7 +56,10 @@ class get_solution_callback_t : public base_solution_callback_t {

class set_solution_callback_t : public base_solution_callback_t {
public:
virtual void set_solution(void* data, void* objective_value) = 0;
virtual void set_solution(void* data,
void* objective_value,
void* solution_bound,
void* user_data) = 0;
base_solution_callback_type get_type() const override
{
return base_solution_callback_type::SET_SOLUTION;
Expand Down
12 changes: 11 additions & 1 deletion cpp/src/dual_simplex/branch_and_bound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ void branch_and_bound_t<i_t, f_t>::report_heuristic(f_t obj)
template <typename i_t, typename f_t>
void branch_and_bound_t<i_t, f_t>::report(char symbol, f_t obj, f_t lower_bound, i_t node_depth)
{
update_user_bound(lower_bound);
i_t nodes_explored = exploration_stats_.nodes_explored;
i_t nodes_unexplored = exploration_stats_.nodes_unexplored;
f_t user_obj = compute_user_objective(original_lp_, obj);
Expand All @@ -300,6 +301,14 @@ void branch_and_bound_t<i_t, f_t>::report(char symbol, f_t obj, f_t lower_bound,
toc(exploration_stats_.start_time));
}

template <typename i_t, typename f_t>
void branch_and_bound_t<i_t, f_t>::update_user_bound(f_t lower_bound)
{
if (user_bound_callback_ == nullptr) { return; }
f_t user_lower = compute_user_objective(original_lp_, lower_bound);
user_bound_callback_(user_lower);
}

template <typename i_t, typename f_t>
void branch_and_bound_t<i_t, f_t>::set_new_solution(const std::vector<f_t>& solution)
{
Expand Down Expand Up @@ -335,9 +344,10 @@ void branch_and_bound_t<i_t, f_t>::set_new_solution(const std::vector<f_t>& solu
num_fractional);
}
}
} else {
settings_.log.debug("Solution objective not better than current upper_bound_. Not accepted.\n");
}
mutex_upper_.unlock();

if (is_feasible) { report_heuristic(obj); }
if (attempt_repair) {
mutex_repair_.lock();
Expand Down
Loading