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
6 changes: 4 additions & 2 deletions docs/C_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ lp_problem_t *create_lp_problem(
const double *con_ub, // constraint upper bounds (length m)
const double *var_lb, // variable lower bounds (length n)
const double *var_ub, // variable upper bounds (length n)
const double *objective_constant // scalar objective offset
const double *objective_constant, // scalar objective offset
const objective_sense_t *objective_sense // objective sense (NULL → minimize)
);

cupdlpx_result_t* solve_lp_problem(
Expand All @@ -32,6 +33,7 @@ cupdlpx_result_t* solve_lp_problem(
- `var_lb`: Variable lower bounds. If `NULL`, defaults to all `-INFINITY`.
- `var_ub`: Variable upper bounds. If `NULL`, defaults to all `+INFINITY`.
- `objective_constant`: Scalar constant term added to the objective value. If `NULL`, defaults to `0.0`.
- `objective_sense`: Objective sense, `OBJECTIVE_SENSE_MINIMIZE` or `OBJECTIVE_SENSE_MAXIMIZE`. If `NULL`, defaults to minimize.


`solve_lp_problem` parameters:
Expand Down Expand Up @@ -70,7 +72,7 @@ int main() {

// Build the problem
lp_problem_t* prob = create_lp_problem(
c, &A_desc, l, u, NULL, NULL, NULL);
c, &A_desc, l, u, NULL, NULL, NULL, NULL);

// Solve (NULL → use default parameters)
cupdlpx_result_t* res = solve_lp_problem(prob, NULL);
Expand Down
3 changes: 2 additions & 1 deletion include/cupdlpx.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ extern "C"
const double *con_ub,
const double *var_lb,
const double *var_ub,
const double *objective_constant);
const double *objective_constant,
const objective_sense_t *objective_sense);

// Set up initial primal and dual solution for an lp_problem_t
void set_start_values(lp_problem_t *prob, const double *primal, const double *dual);
Expand Down
7 changes: 7 additions & 0 deletions include/cupdlpx_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ extern "C"
NORM_TYPE_L_INF = 1
} norm_type_t;

typedef enum
{
OBJECTIVE_SENSE_MINIMIZE = 0,
OBJECTIVE_SENSE_MAXIMIZE = 1
} objective_sense_t;

typedef struct
{
int num_variables;
Expand All @@ -51,6 +57,7 @@ extern "C"
double *variable_upper_bound;
double *objective_vector;
double objective_constant;
objective_sense_t objective_sense;

int *constraint_matrix_row_pointers;
int *constraint_matrix_col_indices;
Expand Down
1 change: 1 addition & 0 deletions internal/internal_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ typedef struct
double *variable_upper_bound;
double *objective_vector;
double objective_constant;
double original_objective_sign;
cu_sparse_matrix_csr_t *constraint_matrix;
cu_sparse_matrix_csr_t *constraint_matrix_t;
double *constraint_lower_bound;
Expand Down
2 changes: 1 addition & 1 deletion internal/solver.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ extern "C"
{
#endif

cupdlpx_result_t *optimize(const pdhg_parameters_t *params, lp_problem_t *original_problem);
cupdlpx_result_t *optimize(const pdhg_parameters_t *params, const lp_problem_t *original_problem);

#ifdef __cplusplus
}
Expand Down
10 changes: 9 additions & 1 deletion internal/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,15 @@ extern "C"

void check_termination_criteria(pdhg_solver_state_t *solver_state, const termination_criteria_t *criteria);

void print_initial_info(const pdhg_parameters_t *params, lp_problem_t *problem);
void print_initial_info(const pdhg_parameters_t *params, const lp_problem_t *problem);

void filter_constraint_matrix_entries(lp_problem_t *out, const lp_problem_t *in, const pdhg_parameters_t *params);

lp_problem_t preprocess_problem(const lp_problem_t *original, const pdhg_parameters_t *params);

void free_preprocessed_problem(const lp_problem_t *preprocessed, const lp_problem_t *original);

void restore_original_objective_sense(cupdlpx_result_t *result, objective_sense_t sense);

void pdhg_final_log(const cupdlpx_result_t *result, const pdhg_parameters_t *params);

Expand Down
21 changes: 9 additions & 12 deletions python/cupdlpx/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,23 +355,20 @@ def optimize(self):
# check model sense
if self.ModelSense not in (PDLP.MINIMIZE, PDLP.MAXIMIZE):
raise ValueError("model_sense must be PDLP.MINIMIZE or PDLP.MAXIMIZE")
# determine sign
sign = 1 if self.ModelSense == PDLP.MINIMIZE else -1
# effective objective based on sense
c_eff = sign * self.c if self.c is not None else None
c0_eff = sign * self.c0 if self.c0 is not None else None
minimize = self.ModelSense == PDLP.MINIMIZE
# call the core solver
info = solve_once(
self.A,
c_eff,
c0_eff,
self.c,
self.c0,
self.lb,
self.ub,
self.constr_lb,
self.constr_ub,
params=self._params,
primal_start=self._primal_start,
dual_start=self._dual_start
dual_start=self._dual_start,
minimize=minimize,
)
# solutions
self._x = np.asarray(info.get("X")) if info.get("X") is not None else None
Expand All @@ -380,8 +377,8 @@ def optimize(self):
# objectives & gaps
primal_obj_eff = info.get("PrimalObj")
dual_obj_eff = info.get("DualObj")
self._objval = sign * primal_obj_eff if primal_obj_eff is not None else None
self._dualobj = sign * dual_obj_eff if dual_obj_eff is not None else None
self._objval = primal_obj_eff if primal_obj_eff is not None else None
self._dualobj = dual_obj_eff if dual_obj_eff is not None else None
self._gap = info.get("ObjectiveGap")
self._rel_gap = info.get("RelativeObjectiveGap")
# status & counters
Expand All @@ -398,8 +395,8 @@ def optimize(self):
self._max_d_ray = info.get("MaxDualRayInfeas")
p_ray_lin_eff = info.get("PrimalRayLinObj")
d_ray_obj_eff = info.get("DualRayObj")
self._p_ray_lin_obj = sign * p_ray_lin_eff if p_ray_lin_eff is not None else None
self._d_ray_obj = sign * d_ray_obj_eff if d_ray_obj_eff is not None else None
self._p_ray_lin_obj = p_ray_lin_eff if p_ray_lin_eff is not None else None
self._d_ray_obj = d_ray_obj_eff if d_ray_obj_eff is not None else None

def _clear_solution_cache(self) -> None:
"""
Expand Down
10 changes: 7 additions & 3 deletions python_bindings/_core_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,8 @@ static py::dict solve_once(py::object A,
py::object constraint_upper_bound, // u (optional → inf)
py::object params = py::none(), // PDHG parameters (optional → default)
py::object primal_start = py::none(), // warm start primal solution (optional)
py::object dual_start = py::none() // warm start dual solution (optional)
py::object dual_start = py::none(), // warm start dual solution (optional)
bool minimize = true // objective sense (true → minimize)
)
{
// parse matrix
Expand Down Expand Up @@ -492,13 +493,15 @@ static py::dict solve_once(py::object A,
}

// build problem
objective_sense_t sense = minimize ? OBJECTIVE_SENSE_MINIMIZE : OBJECTIVE_SENSE_MAXIMIZE;
lp_problem_t *prob = create_lp_problem(c_ptr, // objective vector
&view.desc, // constraint matrix
l_ptr, // constraint lower bound
u_ptr, // constraint upper bound
lb_ptr, // variable lower bound
ub_ptr, // variable upper bound
c0_ptr // objective constant
c0_ptr, // objective constant
&sense // objective sense
);
if (!prob)
{
Expand Down Expand Up @@ -595,5 +598,6 @@ PYBIND11_MODULE(_cupdlpx_core, m)
py::arg("constraint_upper_bound") = py::none(),
py::arg("params") = py::none(),
py::arg("primal_start") = py::none(),
py::arg("dual_start") = py::none());
py::arg("dual_start") = py::none(),
py::arg("minimize") = true);
}
5 changes: 4 additions & 1 deletion src/cupdlpx.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ lp_problem_t *create_lp_problem(const double *objective_c,
const double *con_ub,
const double *var_lb,
const double *var_ub,
const double *objective_constant)
const double *objective_constant,
const objective_sense_t *objective_sense)
{
lp_problem_t *prob = (lp_problem_t *)safe_malloc(sizeof(lp_problem_t));
prob->primal_start = NULL;
Expand Down Expand Up @@ -105,6 +106,7 @@ lp_problem_t *create_lp_problem(const double *objective_c,

// default fill values
prob->objective_constant = objective_constant ? *objective_constant : 0.0;
prob->objective_sense = objective_sense ? *objective_sense : OBJECTIVE_SENSE_MINIMIZE;
fill_or_copy(&prob->objective_vector, prob->num_variables, objective_c, 0.0);
fill_or_copy(&prob->variable_lower_bound, prob->num_variables, var_lb, -INFINITY);
fill_or_copy(&prob->variable_upper_bound, prob->num_variables, var_ub, INFINITY);
Expand All @@ -123,6 +125,7 @@ void cupdlpx_result_free(cupdlpx_result_t *results)

free(results->primal_solution);
free(results->dual_solution);
free(results->reduced_cost);
free(results);
}

Expand Down
58 changes: 35 additions & 23 deletions src/mps_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ typedef struct
char *objective_row_name;
char *current_col_name;
double objective_constant;
bool is_maximize;
objective_sense_t objective_sense;
int error_flag;

} MpsParserState;
Expand Down Expand Up @@ -434,7 +434,7 @@ lp_problem_t *read_mps_file(const char *filename)
if (n_tokens == 0)
continue;

if (n_tokens == 1 && isalpha(tokens[0][0]))
if (isalpha((unsigned char)tokens[0][0]))
{
MpsSection next_section = SEC_NONE;
if (strcmp(tokens[0], "ROWS") == 0)
Expand All @@ -447,32 +447,51 @@ lp_problem_t *read_mps_file(const char *filename)
next_section = SEC_RANGES;
else if (strcmp(tokens[0], "BOUNDS") == 0)
next_section = SEC_BOUNDS;
else if (strcmp(tokens[0], "OBJSENSE") == 0)
else if (strcmp(tokens[0], "OBJSENSE") == 0 || strcmp(tokens[0], "OBJSENS") == 0)
next_section = SEC_OBJSENSE;
else if (strcmp(tokens[0], "ENDATA") == 0)
{
next_section = SEC_ENDATA;
}

if (current_section == SEC_ROWS && next_section != SEC_ROWS && !rows_finalized)
bool inline_max = next_section == SEC_OBJSENSE && n_tokens >= 2 &&
(strcmp(tokens[1], "MAX") == 0 || strcmp(tokens[1], "MAXIMIZE") == 0);
bool inline_min = next_section == SEC_OBJSENSE && n_tokens >= 2 &&
(strcmp(tokens[1], "MIN") == 0 || strcmp(tokens[1], "MINIMIZE") == 0);
bool is_header = next_section != SEC_NONE && (n_tokens == 1 || inline_max || inline_min);

if (is_header)
{
if (finalize_rows(&state) != 0)
state.error_flag = 1;
rows_finalized = true;
}
if (current_section == SEC_ROWS && next_section != SEC_ROWS && !rows_finalized)
{
if (finalize_rows(&state) != 0)
state.error_flag = 1;
rows_finalized = true;
}

current_section = next_section;
if (current_section == SEC_ENDATA)
break;
continue;
current_section = next_section;
if (current_section == SEC_ENDATA)
break;

if (inline_max)
state.objective_sense = OBJECTIVE_SENSE_MAXIMIZE;
else if (inline_min)
state.objective_sense = OBJECTIVE_SENSE_MINIMIZE;

continue;
}
}

switch (current_section)
{
case SEC_OBJSENSE:
if (n_tokens > 0 && (strcmp(tokens[0], "MAX") == 0 || strcmp(tokens[0], "MAXIMIZE") == 0))
if (strcmp(tokens[0], "MAX") == 0 || strcmp(tokens[0], "MAXIMIZE") == 0)
{
state.objective_sense = OBJECTIVE_SENSE_MAXIMIZE;
}
else if (strcmp(tokens[0], "MIN") == 0 || strcmp(tokens[0], "MINIMIZE") == 0)
{
state.is_maximize = true;
state.objective_sense = OBJECTIVE_SENSE_MINIMIZE;
}
break;
case SEC_ROWS:
Expand Down Expand Up @@ -515,7 +534,8 @@ lp_problem_t *read_mps_file(const char *filename)
prob->num_variables = state.col_map.size;
prob->num_constraints = state.row_map.size;
prob->constraint_matrix_num_nonzeros = state.coo_matrix.nnz;
prob->objective_constant = state.is_maximize ? -state.objective_constant : state.objective_constant;
prob->objective_constant = state.objective_constant;
prob->objective_sense = state.objective_sense;

prob->objective_vector = state.objective_coeffs;
prob->variable_lower_bound = state.var_lower_bounds;
Expand All @@ -532,14 +552,6 @@ lp_problem_t *read_mps_file(const char *filename)
state.constraint_lower_bounds = NULL;
state.constraint_upper_bounds = NULL;

if (state.is_maximize)
{
for (int i = 0; i < prob->num_variables; ++i)
{
prob->objective_vector[i] *= -1.0;
}
}

if (mps_coo_to_csr(prob, &state.coo_matrix, prob->num_constraints) != 0)
{
fprintf(stderr, "ERROR: Failed to convert matrix to CSR format.\n");
Expand Down
5 changes: 5 additions & 0 deletions src/presolve.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ lp_problem_t *convert_pslp_to_cupdlpx(PresolvedProblem *reduced_prob, const lp_p
cupdlpx_prob->dual_start = NULL;

cupdlpx_prob->objective_constant = original_prob->objective_constant + reduced_prob->obj_offset;
cupdlpx_prob->objective_sense = original_prob->objective_sense;
cupdlpx_prob->objective_vector = reduced_prob->c;

cupdlpx_prob->constraint_lower_bound = reduced_prob->lhs;
Expand Down Expand Up @@ -188,6 +189,10 @@ void pslp_postsolve(const cupdlpx_presolve_info_t *info, cupdlpx_result_t *resul
result->num_reduced_nonzeros = info->presolver->reduced_prob->nnz;
result->presolve_status = info->presolve_status;

free(result->primal_solution);
free(result->dual_solution);
free(result->reduced_cost);

result->primal_solution = (double *)safe_malloc(original_prob->num_variables * sizeof(double));
result->dual_solution = (double *)safe_malloc(original_prob->num_constraints * sizeof(double));
result->reduced_cost = (double *)safe_malloc(original_prob->num_variables * sizeof(double));
Expand Down
Loading
Loading