Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
79d5157
Change solution to be on host from c++ side
rgsl888prabhu Nov 7, 2025
1ab34ab
fix style
rgsl888prabhu Nov 7, 2025
2160bef
fix
rgsl888prabhu Nov 7, 2025
9ca1325
fix cudf issue
rgsl888prabhu Nov 10, 2025
999a549
fix typo
rgsl888prabhu Nov 10, 2025
a72b785
enable few tests
rgsl888prabhu Nov 10, 2025
1ea0702
remove unsued file
rgsl888prabhu Nov 10, 2025
1a5edbf
Merge branch 'fix_cudf_break' of github.com:rgsl888prabhu/cuopt_publi…
rgsl888prabhu Nov 10, 2025
e735ee1
Merge branch 'main' of github.com:nvidia/cuopt into segfault_issue
rgsl888prabhu Nov 11, 2025
b9d5ea0
Update test_wheel_cuopt.sh
rgsl888prabhu Nov 12, 2025
4a43461
Merge branch 'main' into segfault_issue
rgsl888prabhu Nov 12, 2025
0f4b100
Update test_lp.py
rgsl888prabhu Nov 12, 2025
7511a06
Update utils_wrapper.pyx
rgsl888prabhu Nov 12, 2025
324b412
Update test_lp.py
rgsl888prabhu Nov 12, 2025
816cd21
Merge branch 'release/25.12' into segfault_issue
rgsl888prabhu Nov 17, 2025
582004d
Merge branch 'release/25.12' into segfault_issue
rgsl888prabhu Nov 18, 2025
5e62f50
Merge branch 'release/25.12' into segfault_issue
rgsl888prabhu Nov 19, 2025
d64f387
Merge branch 'release/25.12' into segfault_issue
rgsl888prabhu Nov 20, 2025
2880a7c
Merge branch 'main' into segfault_issue
rgsl888prabhu Jan 5, 2026
6e9f901
Adding best practices and details (#692)
rgsl888prabhu Jan 5, 2026
6632c56
Merge branch 'segfault_issue' of github.com:rgsl888prabhu/cuopt_publi…
rgsl888prabhu Jan 5, 2026
0e66899
fix copyright
rgsl888prabhu Jan 5, 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
26 changes: 13 additions & 13 deletions cpp/include/cuopt/linear_programming/utilities/cython_solve.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ namespace cython {
// aggregate for call_solve() return type
// to be exposed to cython:
struct linear_programming_ret_t {
std::unique_ptr<rmm::device_buffer> primal_solution_;
std::unique_ptr<rmm::device_buffer> dual_solution_;
std::unique_ptr<rmm::device_buffer> reduced_cost_;
std::vector<double> primal_solution_;
std::vector<double> dual_solution_;
std::vector<double> reduced_cost_;
/* -- PDLP Warm Start Data -- */
std::unique_ptr<rmm::device_buffer> current_primal_solution_;
std::unique_ptr<rmm::device_buffer> current_dual_solution_;
std::unique_ptr<rmm::device_buffer> initial_primal_average_;
std::unique_ptr<rmm::device_buffer> initial_dual_average_;
std::unique_ptr<rmm::device_buffer> current_ATY_;
std::unique_ptr<rmm::device_buffer> sum_primal_solutions_;
std::unique_ptr<rmm::device_buffer> sum_dual_solutions_;
std::unique_ptr<rmm::device_buffer> last_restart_duality_gap_primal_solution_;
std::unique_ptr<rmm::device_buffer> last_restart_duality_gap_dual_solution_;
std::vector<double> current_primal_solution_;
std::vector<double> current_dual_solution_;
std::vector<double> initial_primal_average_;
std::vector<double> initial_dual_average_;
std::vector<double> current_ATY_;
std::vector<double> sum_primal_solutions_;
std::vector<double> sum_dual_solutions_;
std::vector<double> last_restart_duality_gap_primal_solution_;
std::vector<double> last_restart_duality_gap_dual_solution_;
double initial_primal_weight_;
double initial_step_size_;
int total_pdlp_iterations_;
Expand All @@ -64,7 +64,7 @@ struct linear_programming_ret_t {
};

struct mip_ret_t {
std::unique_ptr<rmm::device_buffer> solution_;
std::vector<double> solution_;

linear_programming::mip_termination_status_t termination_status_;
error_type_t error_status_;
Expand Down
39 changes: 17 additions & 22 deletions cpp/src/linear_programming/utilities/cython_solve.cu
Original file line number Diff line number Diff line change
Expand Up @@ -131,28 +131,21 @@ linear_programming_ret_t call_solve_lp(
const bool use_pdlp_solver_mode = true;
auto solution = cuopt::linear_programming::solve_lp(
op_problem, solver_settings, problem_checking, use_pdlp_solver_mode, is_batch_mode);

// Convert device vectors to host vectors for LP solution
linear_programming_ret_t lp_ret{
std::make_unique<rmm::device_buffer>(solution.get_primal_solution().release()),
std::make_unique<rmm::device_buffer>(solution.get_dual_solution().release()),
std::make_unique<rmm::device_buffer>(solution.get_reduced_cost().release()),
std::make_unique<rmm::device_buffer>(
solution.get_pdlp_warm_start_data().current_primal_solution_.release()),
std::make_unique<rmm::device_buffer>(
solution.get_pdlp_warm_start_data().current_dual_solution_.release()),
std::make_unique<rmm::device_buffer>(
solution.get_pdlp_warm_start_data().initial_primal_average_.release()),
std::make_unique<rmm::device_buffer>(
solution.get_pdlp_warm_start_data().initial_dual_average_.release()),
std::make_unique<rmm::device_buffer>(
solution.get_pdlp_warm_start_data().current_ATY_.release()),
std::make_unique<rmm::device_buffer>(
solution.get_pdlp_warm_start_data().sum_primal_solutions_.release()),
std::make_unique<rmm::device_buffer>(
solution.get_pdlp_warm_start_data().sum_dual_solutions_.release()),
std::make_unique<rmm::device_buffer>(
solution.get_pdlp_warm_start_data().last_restart_duality_gap_primal_solution_.release()),
std::make_unique<rmm::device_buffer>(
solution.get_pdlp_warm_start_data().last_restart_duality_gap_dual_solution_.release()),
cuopt::host_copy(solution.get_primal_solution()),
cuopt::host_copy(solution.get_dual_solution()),
cuopt::host_copy(solution.get_reduced_cost()),
cuopt::host_copy(solution.get_pdlp_warm_start_data().current_primal_solution_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().current_dual_solution_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_primal_average_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_dual_average_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().current_ATY_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_primal_solutions_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_dual_solutions_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_primal_solution_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_dual_solution_),
Comment on lines +148 to +159
Copy link

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:

#!/bin/bash
# Find host_copy implementation
find . -name "*.h" -o -name "*.hpp" -o -name "*.cu" -o -name "*.cpp" | head -20
echo "---"
# Search for host_copy definition
rg -nP 'inline.*host_copy|^[[:space:]]*host_copy.*{' --type=cpp --type=h -A 10 | head -50

Repository: NVIDIA/cuopt

Length of output: 2174


🏁 Script executed:

#!/bin/bash
# Get full context of the file around lines 148-159
wc -l cpp/src/linear_programming/utilities/cython_solve.cu

Repository: NVIDIA/cuopt

Length of output: 112


🏁 Script executed:

#!/bin/bash
# Show context around lines 148-159
sed -n '130,175p' cpp/src/linear_programming/utilities/cython_solve.cu

Repository: NVIDIA/cuopt

Length of output: 2788


🏁 Script executed:

#!/bin/bash
# Search for host_copy in include files
find cpp/include -name "*.hpp" -o -name "*.h" | xargs grep -l "host_copy"

Repository: NVIDIA/cuopt

Length of output: 40


🏁 Script executed:

#!/bin/bash
# Check for CUDA error checking patterns used in the file
rg -n 'CUDA_CHECK|RAFT_CUDA_TRY|cudaGetLastError|RAFT_CHECK_CUDA|host_copy' cpp/src/linear_programming/utilities/cython_solve.cu | head -30

Repository: NVIDIA/cuopt

Length of output: 1252


🏁 Script executed:

#!/bin/bash
# Get full host_copy implementation
cat -n cpp/src/utilities/copy_helpers.hpp | head -200

Repository: NVIDIA/cuopt

Length of output: 6185


🏁 Script executed:

#!/bin/bash
# Check if host_copy uses error checking internally or is wrapped with error checks
rg -n 'host_copy' cpp/src/utilities/copy_helpers.hpp -B 5 -A 15 | head -100

Repository: NVIDIA/cuopt

Length of output: 3206


🏁 Script executed:

#!/bin/bash
# Look for the default host_copy overload (non-bool version)
rg -n 'template.*host_copy|^[[:space:]]*host_copy.*\{' cpp/src/utilities/copy_helpers.hpp -A 15

Repository: NVIDIA/cuopt

Length of output: 38


🏁 Script executed:

#!/bin/bash
# Check if there's error handling around the solve call
sed -n '140,180p' cpp/src/linear_programming/utilities/cython_solve.cu

Repository: NVIDIA/cuopt

Length of output: 2594


🏁 Script executed:

#!/bin/bash
# Check for async/batched copy patterns in similar code
rg -n 'cudaMemcpyAsync|rmm::device_uvector|thrust::copy' cpp/src/linear_programming/utilities/ --type=cu --type=cpp -C 3 | head -60

Repository: NVIDIA/cuopt

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Check if raft::copy has error checking
rg -n 'raft::copy' cpp/include/cuopt/ --type=cpp --type=h | head -5

Repository: NVIDIA/cuopt

Length of output: 38


🏁 Script executed:

#!/bin/bash
# Verify if this is end-of-solve (not hot path) - check what comes after line 180
sed -n '160,180p' cpp/src/linear_programming/utilities/cython_solve.cu

Repository: NVIDIA/cuopt

Length of output: 1281


🏁 Script executed:

#!/bin/bash
# Check if raft library provides CUDA error checking
find . -type f -name "*.hpp" -o -name "*.h" | xargs grep -l "raft::copy" | head -3

Repository: NVIDIA/cuopt

Length of output: 166


🏁 Script executed:

#!/bin/bash
# Check if there are any CUDA_CHECK or error handlers wrapping these calls
sed -n '130,185p' cpp/src/linear_programming/utilities/cython_solve.cu | grep -i 'cuda\|check\|try\|error'

Repository: NVIDIA/cuopt

Length of output: 311


🏁 Script executed:

#!/bin/bash
# Verify the exact context - is this function expected to return immediately after these copies?
sed -n '175,200p' cpp/src/linear_programming/utilities/cython_solve.cu

Repository: NVIDIA/cuopt

Length of output: 1177


Consider batching device-to-host transfers to reduce redundant synchronizations.

The host_copy function includes an implicit stream_view.synchronize() (line 123 of copy_helpers.hpp). With 12 sequential host_copy calls here, this creates 12 synchronization barriers—one per call—instead of a single barrier. While raft::copy provides built-in CUDA error checking, consolidating the copies into a batched operation with one synchronization at the end would be more efficient:

// Instead of 12 separate host_copy calls with 12 syncs each,
// batch the raft::copy calls first, then synchronize once
🤖 Prompt for AI Agents
In @cpp/src/linear_programming/utilities/cython_solve.cu around lines 148-159,
Multiple sequential calls to cuopt::host_copy (which invokes
stream_view.synchronize() per call) cause repeated synchronization barriers;
instead, aggregate the device->host transfers for the PDLP warm start fields and
other solution arrays by issuing raft::copy (or equivalent device->host memcpy)
into host buffers without synchronizing each time, then call a single
stream/synchronization once after all copies; update the call sites around the
list of cuopt::host_copy(...) for solution.get_primal_solution(),
get_dual_solution(), get_reduced_cost(), and all
solution.get_pdlp_warm_start_data() members to use batched copies and one final
synchronize (referencing host_copy, cuopt::host_copy, raft::copy, and
get_pdlp_warm_start_data() to locate the code).

solution.get_pdlp_warm_start_data().initial_primal_weight_,
Comment on lines +151 to 160
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Skip warm-start host copies when we’re in batch mode.

With this change every PDLP warm-start vector is eagerly host_copy’d, even when is_batch_mode is true. Batch solves never consume those fields (see create_solution(..., is_batch=True)), so we now pay multiple device→host transfers per instance for no benefit. For large LPs that’s a significant regression compared to the old device_buffer path, where batch executions left the data on device.

Wrap these initializers so the copies only occur when !is_batch_mode, e.g.:

@@
-    cuopt::host_copy(solution.get_pdlp_warm_start_data().current_primal_solution_),
-    cuopt::host_copy(solution.get_pdlp_warm_start_data().current_dual_solution_),
-    cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_primal_average_),
-    cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_dual_average_),
-    cuopt::host_copy(solution.get_pdlp_warm_start_data().current_ATY_),
-    cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_primal_solutions_),
-    cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_dual_solutions_),
-    cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_primal_solution_),
-    cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_dual_solution_),
+    is_batch_mode ? std::vector<double>{}
+                  : cuopt::host_copy(solution.get_pdlp_warm_start_data().current_primal_solution_),
+    is_batch_mode ? std::vector<double>{}
+                  : cuopt::host_copy(solution.get_pdlp_warm_start_data().current_dual_solution_),
+    is_batch_mode ? std::vector<double>{}
+                  : cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_primal_average_),
+    is_batch_mode ? std::vector<double>{}
+                  : cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_dual_average_),
+    is_batch_mode ? std::vector<double>{}
+                  : cuopt::host_copy(solution.get_pdlp_warm_start_data().current_ATY_),
+    is_batch_mode ? std::vector<double>{}
+                  : cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_primal_solutions_),
+    is_batch_mode ? std::vector<double>{}
+                  : cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_dual_solutions_),
+    is_batch_mode ? std::vector<double>{}
+                  : cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_primal_solution_),
+    is_batch_mode ? std::vector<double>{}
+                  : cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_dual_solution_),

(or compute the vectors above the initializer and reuse them). That preserves existing semantics while avoiding unnecessary transfers in the batch path.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cuopt::host_copy(solution.get_pdlp_warm_start_data().current_primal_solution_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().current_dual_solution_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_primal_average_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_dual_average_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().current_ATY_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_primal_solutions_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_dual_solutions_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_primal_solution_),
cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_dual_solution_),
solution.get_pdlp_warm_start_data().initial_primal_weight_,
is_batch_mode ? std::vector<double>{}
: cuopt::host_copy(solution.get_pdlp_warm_start_data().current_primal_solution_),
is_batch_mode ? std::vector<double>{}
: cuopt::host_copy(solution.get_pdlp_warm_start_data().current_dual_solution_),
is_batch_mode ? std::vector<double>{}
: cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_primal_average_),
is_batch_mode ? std::vector<double>{}
: cuopt::host_copy(solution.get_pdlp_warm_start_data().initial_dual_average_),
is_batch_mode ? std::vector<double>{}
: cuopt::host_copy(solution.get_pdlp_warm_start_data().current_ATY_),
is_batch_mode ? std::vector<double>{}
: cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_primal_solutions_),
is_batch_mode ? std::vector<double>{}
: cuopt::host_copy(solution.get_pdlp_warm_start_data().sum_dual_solutions_),
is_batch_mode ? std::vector<double>{}
: cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_primal_solution_),
is_batch_mode ? std::vector<double>{}
: cuopt::host_copy(solution.get_pdlp_warm_start_data().last_restart_duality_gap_dual_solution_),
solution.get_pdlp_warm_start_data().initial_primal_weight_,
🤖 Prompt for AI Agents
In cpp/src/linear_programming/utilities/cython_solve.cu around lines 140 to 149,
the PDLP warm-start vectors are unconditionally copied to host causing
unnecessary device→host transfers in batch mode; change the initializer to only
perform cuopt::host_copy calls when !is_batch_mode (or compute the host_copy
results into local variables above the initializer and reuse them), so that when
is_batch_mode is true the original device_buffer path/data is preserved and no
host copies are performed; ensure all referenced warm-start fields are
conditionally set so semantics for non-batch solves remain unchanged.

solution.get_pdlp_warm_start_data().initial_step_size_,
solution.get_pdlp_warm_start_data().total_pdlp_iterations_,
Expand Down Expand Up @@ -194,7 +187,9 @@ mip_ret_t call_solve_mip(
error_type_t::ValidationError,
"MIP solve cannot be called on an LP problem!");
auto solution = cuopt::linear_programming::solve_mip(op_problem, solver_settings);
mip_ret_t mip_ret{std::make_unique<rmm::device_buffer>(solution.get_solution().release()),

// Convert device vector to host vector for MILP solution
mip_ret_t mip_ret{cuopt::host_copy(solution.get_solution()),
solution.get_termination_status(),
solution.get_error_status().get_error_type(),
solution.get_error_status().what(),
Expand Down
26 changes: 13 additions & 13 deletions python/cuopt/cuopt/linear_programming/solver/solver.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,19 @@ cdef extern from "cuopt/linear_programming/pdlp/solver_solution.hpp" namespace "

cdef extern from "cuopt/linear_programming/utilities/cython_solve.hpp" namespace "cuopt::cython": # noqa
cdef cppclass linear_programming_ret_t:
unique_ptr[device_buffer] primal_solution_
unique_ptr[device_buffer] dual_solution_
unique_ptr[device_buffer] reduced_cost_
vector[double] primal_solution_
vector[double] dual_solution_
vector[double] reduced_cost_
# PDLP warm start data
unique_ptr[device_buffer] current_primal_solution_
unique_ptr[device_buffer] current_dual_solution_
unique_ptr[device_buffer] initial_primal_average_
unique_ptr[device_buffer] initial_dual_average_
unique_ptr[device_buffer] current_ATY_
unique_ptr[device_buffer] sum_primal_solutions_
unique_ptr[device_buffer] sum_dual_solutions_
unique_ptr[device_buffer] last_restart_duality_gap_primal_solution_
unique_ptr[device_buffer] last_restart_duality_gap_dual_solution_
vector[double] current_primal_solution_
vector[double] current_dual_solution_
vector[double] initial_primal_average_
vector[double] initial_dual_average_
vector[double] current_ATY_
vector[double] sum_primal_solutions_
vector[double] sum_dual_solutions_
vector[double] last_restart_duality_gap_primal_solution_
vector[double] last_restart_duality_gap_dual_solution_
double initial_primal_weight_
double initial_step_size_
int total_pdlp_iterations_
Expand All @@ -155,7 +155,7 @@ cdef extern from "cuopt/linear_programming/utilities/cython_solve.hpp" namespace
bool solved_by_pdlp_

cdef cppclass mip_ret_t:
unique_ptr[device_buffer] solution_
vector[double] solution_
mip_termination_status_t termination_status_
error_type_t error_status_
string error_message_
Expand Down
138 changes: 32 additions & 106 deletions python/cuopt/cuopt/linear_programming/solver/solver_wrapper.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ from libcpp.string cimport string
from libcpp.utility cimport move
from libcpp.vector cimport vector

from rmm.pylibrmm.device_buffer cimport DeviceBuffer
# DeviceBuffer import removed - now using host vectors directly from C++

from cuopt.linear_programming.data_model.data_model cimport data_model_view_t
from cuopt.linear_programming.data_model.data_model_wrapper cimport DataModel
Expand All @@ -46,18 +46,16 @@ import sys
import warnings
from enum import IntEnum

import cupy as cp
import numpy as np
from numba import cuda

import cudf
from cudf.core.buffer import as_buffer

from cuopt.linear_programming.solver_settings.solver_settings import (
PDLPSolverMode,
SolverSettings,
)
from cuopt.utilities import InputValidationError, col_from_buf
from cuopt.utilities import InputValidationError


cdef extern from "cuopt/linear_programming/utilities/internals.hpp" namespace "cuopt::internals": # noqa
Expand Down Expand Up @@ -299,9 +297,9 @@ cdef create_solution(unique_ptr[solver_ret_t] sol_ret_ptr,
sol_ret = move(sol_ret_ptr.get()[0])

if sol_ret.problem_type == ProblemCategory.MIP or sol_ret.problem_type == ProblemCategory.IP: # noqa
solution = DeviceBuffer.c_from_unique_ptr(
move(sol_ret.mip_ret.solution_)
)
# Extract and copy host solution vector from C++
cdef double[:] solution_view = sol_ret.mip_ret.solution_
solution = np.asarray(solution_view).copy()
termination_status = sol_ret.mip_ret.termination_status_
error_status = sol_ret.mip_ret.error_status_
error_message = sol_ret.mip_ret.error_message_
Expand All @@ -316,11 +314,6 @@ cdef create_solution(unique_ptr[solver_ret_t] sol_ret_ptr,
num_nodes = sol_ret.mip_ret.nodes_
num_simplex_iterations = sol_ret.mip_ret.simplex_iterations_

solution_buf = as_buffer(solution)
solution = cudf.Series._from_column(
col_from_buf(solution_buf, np.float64)
).to_numpy()

return Solution(
ProblemCategory(sol_ret.problem_type),
dict(zip(data_model_obj.get_variable_names(), solution)),
Expand All @@ -341,25 +334,13 @@ cdef create_solution(unique_ptr[solver_ret_t] sol_ret_ptr,
)

else:
primal_solution = DeviceBuffer.c_from_unique_ptr(
move(sol_ret.lp_ret.primal_solution_)
)
dual_solution = DeviceBuffer.c_from_unique_ptr(move(sol_ret.lp_ret.dual_solution_)) # noqa
reduced_cost = DeviceBuffer.c_from_unique_ptr(move(sol_ret.lp_ret.reduced_cost_)) # noqa

primal_solution_buf = as_buffer(primal_solution)
dual_solution_buf = as_buffer(dual_solution)
reduced_cost_buf = as_buffer(reduced_cost)

primal_solution = cudf.Series._from_column(
col_from_buf(primal_solution_buf, np.float64)
).to_numpy()
dual_solution = cudf.Series._from_column(
col_from_buf(dual_solution_buf, np.float64)
).to_numpy()
reduced_cost = cudf.Series._from_column(
col_from_buf(reduced_cost_buf, np.float64)
).to_numpy()
# Extract and copy host solution vectors from C++ for LP
cdef double[:] primal_view = sol_ret.lp_ret.primal_solution_
cdef double[:] dual_view = sol_ret.lp_ret.dual_solution_
cdef double[:] reduced_view = sol_ret.lp_ret.reduced_cost_
primal_solution = np.asarray(primal_view).copy()
dual_solution = np.asarray(dual_view).copy()
reduced_cost = np.asarray(reduced_view).copy()

termination_status = sol_ret.lp_ret.termination_status_
error_status = sol_ret.lp_ret.error_status_
Expand All @@ -375,33 +356,26 @@ cdef create_solution(unique_ptr[solver_ret_t] sol_ret_ptr,

# In BatchSolve, we don't get the warm start data
if not is_batch:
current_primal_solution = DeviceBuffer.c_from_unique_ptr(
move(sol_ret.lp_ret.current_primal_solution_)
)
current_dual_solution = DeviceBuffer.c_from_unique_ptr(
move(sol_ret.lp_ret.current_dual_solution_)
)
initial_primal_average = DeviceBuffer.c_from_unique_ptr(
move(sol_ret.lp_ret.initial_primal_average_)
)
initial_dual_average = DeviceBuffer.c_from_unique_ptr(
move(sol_ret.lp_ret.initial_dual_average_)
)
current_ATY = DeviceBuffer.c_from_unique_ptr(
move(sol_ret.lp_ret.current_ATY_)
)
sum_primal_solutions = DeviceBuffer.c_from_unique_ptr(
move(sol_ret.lp_ret.sum_primal_solutions_)
)
sum_dual_solutions = DeviceBuffer.c_from_unique_ptr(
move(sol_ret.lp_ret.sum_dual_solutions_)
)
last_restart_duality_gap_primal_solution = DeviceBuffer.c_from_unique_ptr( # noqa
move(sol_ret.lp_ret.last_restart_duality_gap_primal_solution_)
)
last_restart_duality_gap_dual_solution = DeviceBuffer.c_from_unique_ptr( # noqa
move(sol_ret.lp_ret.last_restart_duality_gap_dual_solution_)
)
# Extract and copy host warm start data vectors from C++
cdef double[:] curr_primal_view = sol_ret.lp_ret.current_primal_solution_
cdef double[:] curr_dual_view = sol_ret.lp_ret.current_dual_solution_
cdef double[:] init_primal_avg_view = sol_ret.lp_ret.initial_primal_average_
cdef double[:] init_dual_avg_view = sol_ret.lp_ret.initial_dual_average_
cdef double[:] curr_aty_view = sol_ret.lp_ret.current_ATY_
cdef double[:] sum_primal_view = sol_ret.lp_ret.sum_primal_solutions_
cdef double[:] sum_dual_view = sol_ret.lp_ret.sum_dual_solutions_
cdef double[:] last_restart_primal_view = sol_ret.lp_ret.last_restart_duality_gap_primal_solution_
cdef double[:] last_restart_dual_view = sol_ret.lp_ret.last_restart_duality_gap_dual_solution_

current_primal_solution = np.asarray(curr_primal_view).copy()
current_dual_solution = np.asarray(curr_dual_view).copy()
initial_primal_average = np.asarray(init_primal_avg_view).copy()
initial_dual_average = np.asarray(init_dual_avg_view).copy()
current_ATY = np.asarray(curr_aty_view).copy()
sum_primal_solutions = np.asarray(sum_primal_view).copy()
sum_dual_solutions = np.asarray(sum_dual_view).copy()
last_restart_duality_gap_primal_solution = np.asarray(last_restart_primal_view).copy()
last_restart_duality_gap_dual_solution = np.asarray(last_restart_dual_view).copy()
initial_primal_weight = sol_ret.lp_ret.initial_primal_weight_
initial_step_size = sol_ret.lp_ret.initial_step_size_
total_pdlp_iterations = sol_ret.lp_ret.total_pdlp_iterations_
Expand All @@ -411,54 +385,6 @@ cdef create_solution(unique_ptr[solver_ret_t] sol_ret_ptr,
sum_solution_weight = sol_ret.lp_ret.sum_solution_weight_
iterations_since_last_restart = sol_ret.lp_ret.iterations_since_last_restart_ # noqa

current_primal_solution_buf = as_buffer(current_primal_solution)
current_dual_solution_buf = as_buffer(current_dual_solution)
initial_primal_average_buf = as_buffer(initial_primal_average)
initial_dual_average_buf = as_buffer(initial_dual_average)
current_ATY_buf = as_buffer(current_ATY)
sum_primal_solutions_buf = as_buffer(sum_primal_solutions)
sum_dual_solutions_buf = as_buffer(sum_dual_solutions)
last_restart_duality_gap_primal_solution_buf = as_buffer(
last_restart_duality_gap_primal_solution
)
last_restart_duality_gap_dual_solution_buf = as_buffer(
last_restart_duality_gap_dual_solution
)

current_primal_solution = cudf.Series._from_column(
col_from_buf(current_primal_solution_buf, np.float64)
).to_numpy()
current_dual_solution = cudf.Series._from_column(
col_from_buf(current_dual_solution_buf, np.float64)
).to_numpy()
initial_primal_average = cudf.Series._from_column(
col_from_buf(initial_primal_average_buf, np.float64)
).to_numpy()
initial_dual_average = cudf.Series._from_column(
col_from_buf(initial_dual_average_buf, np.float64)
).to_numpy()
current_ATY = cudf.Series._from_column(
col_from_buf(current_ATY_buf, np.float64)
).to_numpy()
sum_primal_solutions = cudf.Series._from_column(
col_from_buf(sum_primal_solutions_buf, np.float64)
).to_numpy()
sum_dual_solutions = cudf.Series._from_column(
col_from_buf(sum_dual_solutions_buf, np.float64)
).to_numpy()
last_restart_duality_gap_primal_solution = cudf.Series._from_column( # noqa
col_from_buf(
last_restart_duality_gap_primal_solution_buf,
np.float64
)
).to_numpy()
last_restart_duality_gap_dual_solution = cudf.Series._from_column(
col_from_buf(
last_restart_duality_gap_dual_solution_buf,
np.float64
)
).to_numpy()

return Solution(
ProblemCategory(sol_ret.problem_type),
dict(zip(data_model_obj.get_variable_names(), primal_solution)), # noqa
Expand Down
Loading