Skip to content

Commit c7f9268

Browse files
committed
add reduced cost in python interface
1 parent 6f74b3c commit c7f9268

3 files changed

Lines changed: 14 additions & 2 deletions

File tree

python/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ print("Status:", m.Status)
7474
print("Objective:", m.ObjVal)
7575
print("Primal solution:", m.X)
7676
print("Dual solution:", m.Pi)
77+
print("Reduced cost:", m.RC)
7778
```
7879

7980
## Modeling
@@ -190,6 +191,7 @@ After calling `m.optimize()`, the solver stores results in a set of read-only at
190191
| `RelGap` | float | Relative primal-dual gap. |
191192
| `X` | numpy.ndarray | Primal solution vector \(x\). May be `None` if no feasible solution was found. |
192193
| `Pi` | numpy.ndarray | Dual solution vector (Lagrange multipliers). |
194+
| `RC` | numpy.ndarray | Reduced Cost vector. |
193195
| `IterCount` | int | Number of iterations performed. |
194196
| `Runtime` | float | Total wall-clock runtime in seconds. |
195197
| `RescalingTime` | float | Time spent on preprocessing and rescaling (seconds). |
@@ -214,6 +216,7 @@ print("Iterations:", m.IterCount, " Runtime (s):", m.Runtime)
214216
# Access solutions
215217
print("Primal solution:", m.X)
216218
print("Dual solution:", m.Pi)
219+
print("Reduced cost:", m.RC)
217220

218221
# Check residuals
219222
print("Primal residual:", m.RelPrimalResidual)

python/cupdlpx/model.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def __init__(
128128
# initialize solution attributes
129129
self._x: Optional[np.ndarray] = None # primal solution
130130
self._y: Optional[np.ndarray] = None # dual solution
131+
self._rc: Optional[np.ndarray] = None # reduced costs
131132
self._objval: Optional[float] = None # objective value
132133
self._dualobj: Optional[float] = None # dual objective value
133134
self._gap: Optional[float] = None # primal-dual gap
@@ -376,6 +377,7 @@ def optimize(self):
376377
# solutions
377378
self._x = np.asarray(info.get("X")) if info.get("X") is not None else None
378379
self._y = np.asarray(info.get("Pi")) if info.get("Pi") is not None else None
380+
self._rc = np.asarray(info.get("RC")) if info.get("RC") is not None else None
379381
# objectives & gaps
380382
primal_obj_eff = info.get("PrimalObj")
381383
dual_obj_eff = info.get("DualObj")
@@ -404,7 +406,7 @@ def _clear_solution_cache(self) -> None:
404406
"""
405407
Clear cached solution attributes.
406408
"""
407-
self._x = self._y = None
409+
self._x = self._y = self._rc = None
408410
self._objval = self._dualobj = None
409411
self._gap = self._rel_gap = None
410412
self._status = None
@@ -423,6 +425,10 @@ def X(self) -> Optional[np.ndarray]:
423425
@property
424426
def Pi(self) -> Optional[np.ndarray]:
425427
return self._y
428+
429+
@property
430+
def RC(self) -> Optional[np.ndarray]:
431+
return self._rc
426432

427433
@property
428434
def ObjVal(self) -> Optional[float]:

python_bindings/_core_bindings.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,16 +518,19 @@ static py::dict solve_once(
518518
const int m_out = res->num_constraints;
519519
py::array_t<double> x({n_out});
520520
py::array_t<double> y({m_out});
521+
py::array_t<double> rc({n_out});
521522
{
522-
auto xb = x.request(), yb = y.request();
523+
auto xb = x.request(), yb = y.request(), rcb = rc.request();
523524
std::memcpy(xb.ptr, res->primal_solution, sizeof(double) * n_out);
524525
std::memcpy(yb.ptr, res->dual_solution, sizeof(double) * m_out);
526+
std::memcpy(rcb.ptr, res->reduced_costs, sizeof(double) * n_out);
525527
}
526528
// build info dict
527529
py::dict info;
528530
// solution
529531
info["X"] = x;
530532
info["Pi"] = y;
533+
info["RC"] = rc;
531534
// objectives and gaps
532535
info["PrimalObj"] = res->primal_objective_value;
533536
info["DualObj"] = res->dual_objective_value;

0 commit comments

Comments
 (0)