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
3 changes: 3 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ print("Status:", m.Status)
print("Objective:", m.ObjVal)
print("Primal solution:", m.X)
print("Dual solution:", m.Pi)
print("Reduced cost:", m.RC)
```

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

# Check residuals
print("Primal residual:", m.RelPrimalResidual)
Expand Down
8 changes: 7 additions & 1 deletion python/cupdlpx/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def __init__(
# initialize solution attributes
self._x: Optional[np.ndarray] = None # primal solution
self._y: Optional[np.ndarray] = None # dual solution
self._rc: Optional[np.ndarray] = None # reduced costs
self._objval: Optional[float] = None # objective value
self._dualobj: Optional[float] = None # dual objective value
self._gap: Optional[float] = None # primal-dual gap
Expand Down Expand Up @@ -376,6 +377,7 @@ def optimize(self):
# solutions
self._x = np.asarray(info.get("X")) if info.get("X") is not None else None
self._y = np.asarray(info.get("Pi")) if info.get("Pi") is not None else None
self._rc = np.asarray(info.get("RC")) if info.get("RC") is not None else None
# objectives & gaps
primal_obj_eff = info.get("PrimalObj")
dual_obj_eff = info.get("DualObj")
Expand Down Expand Up @@ -404,7 +406,7 @@ def _clear_solution_cache(self) -> None:
"""
Clear cached solution attributes.
"""
self._x = self._y = None
self._x = self._y = self._rc = None
self._objval = self._dualobj = None
self._gap = self._rel_gap = None
self._status = None
Expand All @@ -423,6 +425,10 @@ def X(self) -> Optional[np.ndarray]:
@property
def Pi(self) -> Optional[np.ndarray]:
return self._y

@property
def RC(self) -> Optional[np.ndarray]:
return self._rc

@property
def ObjVal(self) -> Optional[float]:
Expand Down
5 changes: 4 additions & 1 deletion python_bindings/_core_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,16 +518,19 @@ static py::dict solve_once(
const int m_out = res->num_constraints;
py::array_t<double> x({n_out});
py::array_t<double> y({m_out});
py::array_t<double> rc({n_out});
{
auto xb = x.request(), yb = y.request();
auto xb = x.request(), yb = y.request(), rcb = rc.request();
std::memcpy(xb.ptr, res->primal_solution, sizeof(double) * n_out);
std::memcpy(yb.ptr, res->dual_solution, sizeof(double) * m_out);
std::memcpy(rcb.ptr, res->reduced_costs, sizeof(double) * n_out);
}
// build info dict
py::dict info;
// solution
info["X"] = x;
info["Pi"] = y;
info["RC"] = rc;
// objectives and gaps
info["PrimalObj"] = res->primal_objective_value;
info["DualObj"] = res->dual_objective_value;
Expand Down
12 changes: 12 additions & 0 deletions src/presolve.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,18 @@ void pslp_postsolve(const cupdlpx_presolve_info_t *info,
memcpy(result->primal_solution, info->presolver->sol->x, original_prob->num_variables * sizeof(double));
memcpy(result->dual_solution, info->presolver->sol->y, original_prob->num_constraints * sizeof(double));
memcpy(result->reduced_cost, info->presolver->sol->z, original_prob->num_variables * sizeof(double));
// TODO: this can be removed if PSLP implements it.
for (int i=0; i <original_prob->num_variables; i++)
{
if (!isfinite(original_prob->variable_lower_bound[i]))
{
result->reduced_cost[i] = fmin(result->reduced_cost[i], 0.0);
}
if (!isfinite(original_prob->variable_upper_bound[i]))
{
result->reduced_cost[i] = fmax(result->reduced_cost[i], 0.0);
}
}
result->presolve_time = info->presolve_time;
if (info->presolver->reduced_prob->n == 0)
{
Expand Down
32 changes: 20 additions & 12 deletions src/solver.cu
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ __global__ void compute_delta_solution_kernel(
const double *__restrict__ initial_dual,
const double *__restrict__ pdhg_dual, double *__restrict__ delta_dual,
int n_vars, int n_cons);
static void rescale_solution(pdhg_solver_state_t *state);
static cupdlpx_result_t *create_result_from_state(pdhg_solver_state_t *state, const lp_problem_t *original_problem);
static void perform_restart(pdhg_solver_state_t *state, const pdhg_parameters_t *params);
static void initialize_step_size_and_primal_weight(pdhg_solver_state_t *state, const pdhg_parameters_t *params);
Expand Down Expand Up @@ -290,12 +289,24 @@ __global__ void compute_and_rescale_reduced_cost_kernel(
const double *__restrict__ variable_rescaling,
const double objective_vector_rescaling,
const double constraint_bound_rescaling,
const double *__restrict__ variable_lower_bound,
const double *__restrict__ variable_upper_bound,
int n_vars)
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n_vars)
{
reduced_cost[i] = (objective[i] - dual_product[i]) * variable_rescaling[i] / objective_vector_rescaling;
double rc = (objective[i] - dual_product[i]) * variable_rescaling[i] / objective_vector_rescaling;

if (!isfinite(variable_lower_bound[i]))
{
rc = fmin(rc, 0.0);
}
if (!isfinite(variable_upper_bound[i]))
{
rc = fmax(rc, 0.0);
}
reduced_cost[i] = rc;
}
}

Expand Down Expand Up @@ -902,15 +913,6 @@ static void compute_next_dual_solution(pdhg_solver_state_t *state,const int k_of
}
}

static void rescale_solution(pdhg_solver_state_t *state)
{
rescale_solution_kernel<<<state->num_blocks_primal_dual, THREADS_PER_BLOCK, 0, state->stream>>>(
state->pdhg_primal_solution, state->pdhg_dual_solution,
state->variable_rescaling, state->constraint_rescaling,
state->objective_vector_rescaling, state->constraint_bound_rescaling,
state->num_variables, state->num_constraints);
}

static void perform_restart(pdhg_solver_state_t *state,
const pdhg_parameters_t *params)
{
Expand Down Expand Up @@ -1202,9 +1204,15 @@ static cupdlpx_result_t *create_result_from_state(pdhg_solver_state_t *state, co
state->variable_rescaling,
state->objective_vector_rescaling,
state->constraint_bound_rescaling,
state->variable_lower_bound,
state->variable_upper_bound,
state->num_variables);

rescale_solution(state);
rescale_solution_kernel<<<state->num_blocks_primal_dual, THREADS_PER_BLOCK, 0, state->stream>>>(
state->pdhg_primal_solution, state->pdhg_dual_solution,
state->variable_rescaling, state->constraint_rescaling,
state->objective_vector_rescaling, state->constraint_bound_rescaling,
state->num_variables, state->num_constraints);

results->primal_solution =
(double *)safe_malloc(state->num_variables * sizeof(double));
Expand Down