Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
94 changes: 56 additions & 38 deletions cpp/src/barrier/barrier.cu
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <barrier/device_sparse_matrix.cuh>
#include <barrier/iterative_refinement.hpp>
#include <barrier/pinned_host_allocator.hpp>
#include <barrier/saved_iterate.hpp>
#include <barrier/second_order_cone_kernels.cuh>
#include <barrier/sparse_cholesky.cuh>
#include <barrier/sparse_matrix_kernels.cuh>
Expand Down Expand Up @@ -504,6 +505,7 @@ class iteration_data_t {
relative_primal_residual_save(inf),
relative_dual_residual_save(inf),
relative_complementarity_residual_save(inf),
relative_objective_gap_save(inf),
primal_residual_norm_save(inf),
dual_residual_norm_save(inf),
complementarity_residual_norm_save(inf),
Expand Down Expand Up @@ -1007,6 +1009,7 @@ class iteration_data_t {
relative_primal_residual_save = inf;
relative_dual_residual_save = inf;
relative_complementarity_residual_save = inf;
relative_objective_gap_save = inf;
primal_residual_norm_save = inf;
dual_residual_norm_save = inf;
complementarity_residual_norm_save = inf;
Expand Down Expand Up @@ -2303,6 +2306,7 @@ class iteration_data_t {
f_t relative_primal_residual_save;
f_t relative_dual_residual_save;
f_t relative_complementarity_residual_save;
f_t relative_objective_gap_save;
f_t primal_residual_norm_save;
f_t dual_residual_norm_save;
f_t complementarity_residual_norm_save;
Expand Down Expand Up @@ -4436,11 +4440,12 @@ lp_status_t barrier_solver_t<i_t, f_t>::check_for_suboptimal_solution(
dual_objective_save -= quad_objective;
}

f_t objective_gap_save = std::abs(primal_objective_save - dual_objective_save);
f_t user_primal_objective_save = compute_user_objective(lp, primal_objective_save);
f_t relative_objective_gap_save =
objective_gap_save /
(1.0 + std::min(std::abs(user_primal_objective_save), std::abs(primal_objective_save)));
f_t objective_gap_save, relative_objective_gap_save;
compute_objective_gap(lp,
primal_objective_save,
dual_objective_save,
objective_gap_save,
relative_objective_gap_save);
bool small_gap_save =
(!data.has_cones() && data.Q.n == 0) ||
relative_objective_gap_save < settings.barrier_relaxed_relative_objective_gap_tol;
Expand Down Expand Up @@ -4485,7 +4490,7 @@ lp_status_t barrier_solver_t<i_t, f_t>::check_for_suboptimal_solution(
relative_complementarity_residual,
relative_objective_gap);
}
settings.log.printf("Search direction computation failed\n");
settings.log.printf("Current and saved solutions do not satisfy relaxed barrier tolerances\n");

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.

You probably want to keep "Search direction computation failed" that explains why we need to stop the solver.

Maybe "Search direction computation failed. No solutions satisfy barrier tolerances"

return lp_status_t::NUMERICAL_ISSUES;
}

Expand Down Expand Up @@ -4667,6 +4672,7 @@ lp_status_t barrier_solver_t<i_t, f_t>::barrier_advanced_solve(f_t start_time,
}

if (status < 0) {
settings.log.printf("Search direction computation failed (affine step)\n");
return check_for_suboptimal_solution(data,
start_time,
iter,
Expand Down Expand Up @@ -4709,6 +4715,7 @@ lp_status_t barrier_solver_t<i_t, f_t>::barrier_advanced_solve(f_t start_time,
return lp_status_t::CONCURRENT_LIMIT;
}
if (status < 0) {
settings.log.printf("Search direction computation failed (corrector step)\n");
return check_for_suboptimal_solution(data,
start_time,
iter,
Expand Down Expand Up @@ -4763,38 +4770,49 @@ lp_status_t barrier_solver_t<i_t, f_t>::barrier_advanced_solve(f_t start_time,
data.use_high_accuracy_ir = true;
}

if (relative_primal_residual < settings.barrier_relaxed_feasibility_tol &&
relative_dual_residual < settings.barrier_relaxed_optimality_tol &&
relative_complementarity_residual < settings.barrier_relaxed_complementarity_tol) {
if (relative_primal_residual < data.relative_primal_residual_save &&
relative_dual_residual < data.relative_dual_residual_save &&
relative_complementarity_residual < data.relative_complementarity_residual_save &&
primal_objective == primal_objective && dual_objective == dual_objective) {
settings.log.debug(
"Saving solution at iter %d: feasibility %.2e, optimality %.2e, complementarity "
"%.2e\n",
iter,
relative_primal_residual,
relative_dual_residual,
relative_complementarity_residual);
raft::copy(data.w.data(), data.d_w_.data(), data.d_w_.size(), stream_view_);
raft::copy(data.x.data(), data.d_x_.data(), data.d_x_.size(), stream_view_);
raft::copy(data.y.data(), data.d_y_.data(), data.d_y_.size(), stream_view_);
raft::copy(data.v.data(), data.d_v_.data(), data.d_v_.size(), stream_view_);
raft::copy(data.z.data(), data.d_z_.data(), data.d_z_.size(), stream_view_);
stream_view_.sync();
data.w_save = data.w;
data.x_save = data.x;
data.y_save = data.y;
data.v_save = data.v;
data.z_save = data.z;
data.relative_primal_residual_save = relative_primal_residual;
data.relative_dual_residual_save = relative_dual_residual;
data.relative_complementarity_residual_save = relative_complementarity_residual;
data.primal_residual_norm_save = primal_residual_norm;
data.dual_residual_norm_save = dual_residual_norm;
data.complementarity_residual_norm_save = complementarity_residual_norm;
}
const iterate_residuals_t<f_t> current_residuals{relative_primal_residual,
relative_dual_residual,
relative_complementarity_residual,
relative_objective_gap};
const iterate_residuals_t<f_t> saved_residuals{data.relative_primal_residual_save,
data.relative_dual_residual_save,
data.relative_complementarity_residual_save,
data.relative_objective_gap_save};
const iterate_residuals_t<f_t> relaxed_tolerances{
settings.barrier_relaxed_feasibility_tol,
settings.barrier_relaxed_optimality_tol,
settings.barrier_relaxed_complementarity_tol,
settings.barrier_relaxed_relative_objective_gap_tol};
if (should_save_iterate(current_residuals,
saved_residuals,
relaxed_tolerances,
data.has_cones() || data.Q.n > 0) &&
std::isfinite(primal_objective) && std::isfinite(dual_objective)) {
settings.log.debug(
"Saving solution at iter %d: feasibility %.2e, optimality %.2e, complementarity "
"%.2e\n",
iter + 1,
relative_primal_residual,
relative_dual_residual,
relative_complementarity_residual);
raft::copy(data.w.data(), data.d_w_.data(), data.d_w_.size(), stream_view_);
raft::copy(data.x.data(), data.d_x_.data(), data.d_x_.size(), stream_view_);
raft::copy(data.y.data(), data.d_y_.data(), data.d_y_.size(), stream_view_);
raft::copy(data.v.data(), data.d_v_.data(), data.d_v_.size(), stream_view_);
raft::copy(data.z.data(), data.d_z_.data(), data.d_z_.size(), stream_view_);
stream_view_.sync();
data.w_save = data.w;
data.x_save = data.x;
data.y_save = data.y;
data.v_save = data.v;
data.z_save = data.z;
data.relative_primal_residual_save = relative_primal_residual;
data.relative_dual_residual_save = relative_dual_residual;
data.relative_complementarity_residual_save = relative_complementarity_residual;
data.relative_objective_gap_save = relative_objective_gap;
data.primal_residual_norm_save = primal_residual_norm;
data.dual_residual_norm_save = dual_residual_norm;
data.complementarity_residual_norm_save = complementarity_residual_norm;
}

iter++;
Expand Down
53 changes: 53 additions & 0 deletions cpp/src/barrier/saved_iterate.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* 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 <algorithm>
#include <cmath>

namespace cuopt::mathematical_optimization::barrier {

template <typename f_t>
struct iterate_residuals_t {
f_t primal;
f_t dual;
f_t complementarity;
f_t objective_gap;
};

template <typename f_t>
bool should_save_iterate(const iterate_residuals_t<f_t>& current,
const iterate_residuals_t<f_t>& saved,
const iterate_residuals_t<f_t>& tolerance,
bool check_objective_gap)
{
const auto satisfies_tolerance = [](f_t residual, f_t tolerance) {
return std::isfinite(residual) && std::isfinite(tolerance) && tolerance > 0 &&
residual < tolerance;
};

// Only consider candidates that satisfy every enabled relaxed tolerance.
if (!satisfies_tolerance(current.primal, tolerance.primal) ||
!satisfies_tolerance(current.dual, tolerance.dual) ||
!satisfies_tolerance(current.complementarity, tolerance.complementarity) ||
(check_objective_gap &&
!satisfies_tolerance(current.objective_gap, tolerance.objective_gap))) {
return false;
}

// Rank eligible candidates by their worst residual.
f_t current_merit = std::max({current.primal, current.dual, current.complementarity});
f_t saved_merit = std::max({saved.primal, saved.dual, saved.complementarity});
if (check_objective_gap) {
current_merit = std::max(current_merit, current.objective_gap);
saved_merit = std::max(saved_merit, saved.objective_gap);
}

return current_merit < saved_merit;
}

} // namespace cuopt::mathematical_optimization::barrier
Loading