Skip to content
Open
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
8 changes: 6 additions & 2 deletions python/cuopt/cuopt/linear_programming/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1900,7 +1900,11 @@ def relax(self):
def populate_solution(self, solution):
self.Status = solution.get_termination_status()
self.SolveTime = solution.get_solve_time()
self.warmstart_data = solution.get_pdlp_warm_start_data()
self.warmstart_data = (
solution.get_pdlp_warm_start_data()
if solution.problem_category == 0
else None
)

IsMIP = False
if solution.problem_category == 0:
Expand All @@ -1909,7 +1913,7 @@ def populate_solution(self, solution):
IsMIP = True
self.SolutionStats = self.dict_to_object(solution.get_milp_stats())
primal_sol = solution.get_primal_solution()
reduced_cost = solution.get_reduced_cost()
reduced_cost = solution.get_reduced_cost() if not IsMIP else None
if len(primal_sol) > 0:
for var in self.vars:
var.Value = primal_sol[var.index]
Expand Down
65 changes: 40 additions & 25 deletions python/cuopt/cuopt/linear_programming/solution/solution.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 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

from cuopt.linear_programming.solver.solver_wrapper import (
Expand Down Expand Up @@ -167,25 +167,28 @@ def __init__(
self.problem_category = problem_category
self.primal_solution = primal_solution
self.dual_solution = dual_solution
self.pdlp_warm_start_data = PDLPWarmStartData(
current_primal_solution,
current_dual_solution,
initial_primal_average,
initial_dual_average,
current_ATY,
sum_primal_solutions,
sum_dual_solutions,
last_restart_duality_gap_primal_solution,
last_restart_duality_gap_dual_solution,
initial_primal_weight,
initial_step_size,
total_pdlp_iterations,
total_pdhg_iterations,
last_candidate_kkt_score,
last_restart_kkt_score,
sum_solution_weight,
iterations_since_last_restart,
)
if problem_category == ProblemCategory.LP:
self.pdlp_warm_start_data = PDLPWarmStartData(
current_primal_solution,
current_dual_solution,
initial_primal_average,
initial_dual_average,
current_ATY,
sum_primal_solutions,
sum_dual_solutions,
last_restart_duality_gap_primal_solution,
last_restart_duality_gap_dual_solution,
initial_primal_weight,
initial_step_size,
total_pdlp_iterations,
total_pdhg_iterations,
last_candidate_kkt_score,
last_restart_kkt_score,
sum_solution_weight,
iterations_since_last_restart,
)
else:
self.pdlp_warm_start_data = None
self._set_termination_status(termination_status)
self.error_status = error_status
self.error_message = error_message
Expand Down Expand Up @@ -216,8 +219,17 @@ def __init__(
def _set_termination_status(self, ts):
if self.problem_category == ProblemCategory.LP:
self.termination_status = LPTerminationStatus(ts)
else:
elif self.problem_category in (
ProblemCategory.MIP,
ProblemCategory.IP,
):
self.termination_status = MILPTerminationStatus(ts)
else:
raise ValueError(
f"Unknown problem_category: {self.problem_category!r}. "
"Expected one of ProblemCategory.LP, ProblemCategory.MIP, "
"ProblemCategory.IP."
)

def raise_if_milp_solution(self, function_name):
if self.problem_category in (ProblemCategory.MIP, ProblemCategory.IP):
Expand All @@ -242,7 +254,7 @@ def get_dual_solution(self):
Note: Applicable to only LP
Returns the dual solution as numpy.array with float64 type.
"""
self.raise_if_milp_solution(__name__)
self.raise_if_milp_solution("get_dual_solution")
return self.dual_solution

def get_primal_objective(self):
Expand All @@ -256,7 +268,7 @@ def get_dual_objective(self):
Note: Applicable to only LP
Returns the dual objective as a float64.
"""
self.raise_if_milp_solution(__name__)
self.raise_if_milp_solution("get_dual_objective")
return self.dual_objective

def get_termination_status(self):
Expand Down Expand Up @@ -325,14 +337,16 @@ def get_lp_stats(self):
Number of iterations the LP solver did before converging.
"""

self.raise_if_milp_solution(__name__)
self.raise_if_milp_solution("get_lp_stats")

return self.lp_stats

def get_reduced_cost(self):
"""
Note: Applicable to only LP
Returns the reduced cost as numpy.array with float64 type.
"""
self.raise_if_milp_solution("get_reduced_cost")
return self.reduced_cost

def get_pdlp_warm_start_data(self):
Expand All @@ -343,6 +357,7 @@ def get_pdlp_warm_start_data(self):

See `SolverSettings.set_pdlp_warm_start_data` for more details.
"""
self.raise_if_milp_solution("get_pdlp_warm_start_data")
return self.pdlp_warm_start_data

def get_milp_stats(self):
Expand Down Expand Up @@ -386,7 +401,7 @@ def get_milp_stats(self):
Number of simplex iterations performed during the MIP solve
"""

self.raise_if_lp_solution(__name__)
self.raise_if_lp_solution("get_milp_stats")

return self.milp_stats

Expand Down