Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@

#include <cuda/std/span>


namespace cuopt::cython {
class barrier_cache_t;
Comment thread
Iroy30 marked this conversation as resolved.
Outdated
}

namespace cuopt {
namespace CUOPT_EXPORT mathematical_optimization {

Expand Down Expand Up @@ -356,6 +361,10 @@ class pdlp_solver_settings_t {
// Used to force batch PDLP to solve a subbatch of the problems at a time
// The 0 default value will make the solver use its heuristic to determine the subbatch size
i_t fixed_batch_size{0};
/** When true, first GPU barrier/QCQP solve returns a ``barrier_cache_t`` capsule. */
bool sequence_solve{false};
/** Non-owning cache pointer set by ``call_solve`` for barrier symbolic reuse. */
cuopt::cython::barrier_cache_t* barrier_cache{nullptr};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we want to store barrier_cache in solver_settings? Maybe this should live in a solution object or in the model?

@Iroy30 Iroy30 Sep 2, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

On first solve return, cache comes back on the solution (lp_ret.barrier_cache) & python owns/stores it on DataModel.
Next solve: cache from python DataModel -> call_solve -> call_solve_lp -> solve_lp -> solve_qcqp -> barrier_advanced_solve. This way all APIs stay unchanged.

Alternatives:

  1. Put cache on optimization_problem_t and user_problem_t and trickle it down.
  2. Or, add a additional cache argument to call_solve_lp, solve_lp, solve_qcqp.

Settings is the smallest and least invasive API change. we can do 1 or 2 as well


private:
/** Initial primal solution */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* clang-format off */
/*
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
/* clang-format on */

#pragma once

#include <cuopt/export.hpp>

#include <memory>

#include <raft/core/handle.hpp>
#include <rmm/cuda_stream.hpp>

namespace cuopt::mathematical_optimization::barrier {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we should expose this namespace at this level

template <typename i_t, typename f_t>
class iteration_data_t;

@rg20 rg20 Aug 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

iteration_data_t is too specific to our implementation of barrier.

It can be just solver_cache_t, which makes it easy to add more things.


void destroy_iteration_data(iteration_data_t<int, double>* data);

void apply_barrier_linear_objective(iteration_data_t<int, double>& data,
double const* barrier_c,
int n);
} // namespace cuopt::mathematical_optimization::barrier

namespace cuopt {
namespace CUOPT_EXPORT cython {
Comment thread
Iroy30 marked this conversation as resolved.
Outdated

struct barrier_transform_t;

/**
* @brief GPU solve cache owned by DataModel when sequence_solve is on.
*
* After an Optimal full solve, holds iteration_data_t and the user↔barrier transform.
Comment thread
Iroy30 marked this conversation as resolved.
Outdated
* update_q crushes the new linear objective and sets c_dirty so the next Solve
Comment thread
Iroy30 marked this conversation as resolved.
Outdated
* reuses that workspace (skip convert/presolve/scaling).
*/
class barrier_cache_t {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this should probably be

template <typename i_t, typename f_t>
class barrier_cache_t`

And then within the class you should use i_t for int and f_t for double.

public:
static std::unique_ptr<barrier_cache_t> create(unsigned stream_flags);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

barrier_cache_t(barrier_cache_t&&) noexcept;
barrier_cache_t& operator=(barrier_cache_t&&) noexcept;
~barrier_cache_t();

[[nodiscard]] raft::handle_t* handle_ptr();
[[nodiscard]] raft::handle_t const* handle_ptr() const;
[[nodiscard]] rmm::cuda_stream_view stream_view() const;

/** Drop cached iteration workspace and transform (handle/stream stay). */
void clear();

/**
* @brief Take ownership of barrier iteration workspace. @p data may be null (clears).
*/
void store_iteration_data(
mathematical_optimization::barrier::iteration_data_t<int, double>* data);

/**
* @brief Release ownership of cached iteration workspace; caller must delete or wrap it.
*/
mathematical_optimization::barrier::iteration_data_t<int, double>* release_iteration_data();

void store_transform(std::unique_ptr<barrier_transform_t> transform);
[[nodiscard]] barrier_transform_t* transform();
[[nodiscard]] barrier_transform_t const* transform() const;
void set_c_dirty(bool dirty);
[[nodiscard]] bool c_dirty() const;

/**
* Crush user-space linear objective into cached iteration_data_t.c / d_c_ and set c_dirty.
* Requires a stored transform and iteration_data from an Optimal solve.
*/
void update_linear_objective(double const* c, int n);

private:
barrier_cache_t(std::unique_ptr<rmm::cuda_stream> stream,
std::unique_ptr<raft::handle_t> handle);

struct impl;
std::unique_ptr<impl> impl_;
};

} // namespace CUOPT_EXPORT cython
} // namespace cuopt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include <cuopt/mathematical_optimization/optimization_problem_solution_interface.hpp>
#include <cuopt/mathematical_optimization/solver_settings.hpp>
#include <cuopt/mathematical_optimization/utilities/cython_types.hpp>

#include <cuopt/mathematical_optimization/io/data_model_view.hpp>
#include <memory>
#include <raft/core/handle.hpp>
Expand Down Expand Up @@ -56,7 +55,8 @@ std::unique_ptr<solver_ret_t> call_solve(
cuopt::mathematical_optimization::io::data_model_view_t<int, double>*,
mathematical_optimization::solver_settings_t<int, double>*,
unsigned int flags = cudaStreamNonBlocking,
bool is_batch_mode = false);
bool is_batch_mode = false,
barrier_cache_t* cache_in = nullptr);

std::pair<std::vector<std::unique_ptr<solver_ret_t>>, double> solve_batch_remote(
std::vector<cuopt::mathematical_optimization::io::data_model_view_t<int, double>*>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <cuopt/mathematical_optimization/mip/solver_solution.hpp>
#include <cuopt/mathematical_optimization/pdlp/solver_solution.hpp>
#include <cuopt/mathematical_optimization/utilities/internals.hpp>
#include <cuopt/mathematical_optimization/utilities/barrier_cache.hpp>

#include <rmm/device_buffer.hpp>

Expand Down Expand Up @@ -86,6 +87,9 @@ struct linear_programming_ret_t {
double solve_time_{};
mathematical_optimization::method_t solved_by_{};

/** GPU barrier cache (stream + handle + iteration workspace); moved to Python capsule when set. */
std::unique_ptr<barrier_cache_t> barrier_cache;

bool is_gpu() const { return std::holds_alternative<gpu_solutions_t>(solutions_); }
};

Expand Down
Loading
Loading