From d98d3b3d840c0dc149a7213ccca985bb6cafcc27 Mon Sep 17 00:00:00 2001 From: Zedong Peng Date: Sat, 20 Dec 2025 17:19:35 -0500 Subject: [PATCH 1/4] add support for PSLP v0.0.3 --- CMakeLists.txt | 2 +- src/presolve.c | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 324f1b2..c5e9700 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ endif() include(FetchContent) -set(PSLP_VERSION_TAG "v0.0.2") +set(PSLP_VERSION_TAG "v0.0.3") FetchContent_Declare( pslp diff --git a/src/presolve.c b/src/presolve.c index aa9ffef..27d5379 100644 --- a/src/presolve.c +++ b/src/presolve.c @@ -21,8 +21,6 @@ const char *get_presolve_status_str(enum PresolveStatus_ status) return "UNCHANGED"; case REDUCED: return "REDUCED"; - case UNBOUNDED: - return "UNBOUNDED"; case INFEASIBLE: return "INFEASIBLE"; case UNBNDORINFEAS: @@ -104,7 +102,7 @@ cupdlpx_presolve_info_t *pslp_presolve(const lp_problem_t *original_prob, const printf(" %-15s : %.3g sec\n", "presolve time", info->presolve_time); } - if (status & INFEASIBLE || status & UNBOUNDED) + if (status & INFEASIBLE || status & UNBNDORINFEAS) { info->problem_solved_during_presolve = true; info->reduced_problem = NULL; @@ -134,10 +132,6 @@ cupdlpx_result_t *create_result_from_presolve(const cupdlpx_presolve_info_t *inf { result->termination_reason = TERMINATION_REASON_PRIMAL_INFEASIBLE; } - else if (info->presolve_status == UNBOUNDED) - { - result->termination_reason = TERMINATION_REASON_DUAL_INFEASIBLE; - } else if (info->presolve_status == UNBNDORINFEAS) { result->termination_reason = TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED; From 8160abbfe780160737d1ad4a6dd1cff10fd7585f Mon Sep 17 00:00:00 2001 From: Zedong Peng Date: Sun, 21 Dec 2025 20:49:05 -0500 Subject: [PATCH 2/4] add presolve_stats in results --- include/cupdlpx_types.h | 1 + src/cli.c | 17 +++++++++++++++++ src/presolve.c | 5 +++++ src/solver.cu | 11 ++++++++--- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/include/cupdlpx_types.h b/include/cupdlpx_types.h index 9844133..a52793a 100644 --- a/include/cupdlpx_types.h +++ b/include/cupdlpx_types.h @@ -115,6 +115,7 @@ extern "C" double cumulative_time_sec; double presolve_time; int presolve_status; + PresolveStats presolve_stats; double absolute_primal_residual; double relative_primal_residual; diff --git a/src/cli.c b/src/cli.c index ed18ff5..398e299 100644 --- a/src/cli.c +++ b/src/cli.c @@ -124,6 +124,23 @@ void save_solver_summary(const cupdlpx_result_t *result, const char *output_dir, fprintf(outfile, "Reduced Rows: %d\n", result->num_reduced_constraints); fprintf(outfile, "Reduced Columns: %d\n", result->num_reduced_variables); fprintf(outfile, "Reduced Nonzeros: %d\n", result->num_reduced_nonzeros); + + if (result->presolve_stats.n_cols_original > 0) { + fprintf(outfile, "NNZ Removed Trivial: %d\n", result->presolve_stats.nnz_removed_trivial); + fprintf(outfile, "NNZ Removed Fast: %d\n", result->presolve_stats.nnz_removed_fast); + fprintf(outfile, "NNZ Removed Primal Propagation: %d\n", result->presolve_stats.nnz_removed_primal_propagation); + fprintf(outfile, "NNZ Removed Parallel Rows: %d\n", result->presolve_stats.nnz_removed_parallel_rows); + fprintf(outfile, "NNZ Removed Parallel Cols: %d\n", result->presolve_stats.nnz_removed_parallel_cols); + + fprintf(outfile, "Presolve Time Init (sec): %e\n", result->presolve_stats.time_init); + fprintf(outfile, "Presolve Time Run (sec): %e\n", result->presolve_stats.time_presolve); + fprintf(outfile, "Presolve Time Fast (sec): %e\n", result->presolve_stats.time_fast_reductions); + fprintf(outfile, "Presolve Time Medium (sec): %e\n", result->presolve_stats.time_medium_reductions); + fprintf(outfile, "Presolve Time Primal Proppagation (sec): %e\n", result->presolve_stats.time_primal_propagation); + fprintf(outfile, "Presolve Time Parallel Rows (sec): %e\n", result->presolve_stats.time_parallel_rows); + fprintf(outfile, "Presolve Time Parallel Cols (sec): %e\n", result->presolve_stats.time_parallel_cols); + fprintf(outfile, "Postsolve Time (sec): %e\n", result->presolve_stats.time_postsolve); + } } if (result->feasibility_polishing_time > 0.0) { diff --git a/src/presolve.c b/src/presolve.c index 27d5379..bffe68b 100644 --- a/src/presolve.c +++ b/src/presolve.c @@ -148,6 +148,7 @@ cupdlpx_result_t *create_result_from_presolve(const cupdlpx_presolve_info_t *inf result->num_reduced_nonzeros = info->presolver->reduced_prob->nnz; result->presolve_status = info->presolve_status; result->presolve_time = info->presolve_time; + result->presolve_stats = *(info->presolver->stats); // TODO: Verify if setting solution pointers to NULL affects Python/Julia bindings. if (result->num_variables > 0) { @@ -185,6 +186,10 @@ void pslp_postsolve(cupdlpx_presolve_info_t *info, memcpy(result->reduced_cost, info->presolver->sol->z, original_prob->num_variables * sizeof(double)); result->primal_objective_value = info->presolver->sol->obj; result->presolve_time = info->presolve_time; + // result->presolve_stats = *(info->presolver->stats); + if (info->presolver->stats != NULL) { + result->presolve_stats = *(info->presolver->stats); + } } void cupdlpx_presolve_info_free(cupdlpx_presolve_info_t *info) diff --git a/src/solver.cu b/src/solver.cu index d938a6a..39889c4 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -66,7 +66,7 @@ static void compute_next_pdhg_dual_solution(pdhg_solver_state_t *state); static void halpern_update(pdhg_solver_state_t *state, double reflection_coefficient); 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 cupdlpx_result_t *create_result_from_state(pdhg_solver_state_t *state, const lp_problem_t *original_problem, PresolveStats *presolve_stats); static void perform_restart(pdhg_solver_state_t *state, const pdhg_parameters_t *params); static void @@ -187,7 +187,7 @@ cupdlpx_result_t *optimize(const pdhg_parameters_t *params, feasibility_polish(params, state); } - cupdlpx_result_t *result = create_result_from_state(state, original_problem); + cupdlpx_result_t *result = create_result_from_state(state, original_problem, presolve_info->presolver->stats); if (params->presolve && presolve_info) { @@ -982,7 +982,7 @@ void rescale_info_free(rescale_info_t *info) free(info); } -static cupdlpx_result_t *create_result_from_state(pdhg_solver_state_t *state, const lp_problem_t *original_problem) +static cupdlpx_result_t *create_result_from_state(pdhg_solver_state_t *state, const lp_problem_t *original_problem, PresolveStats *presolve_stats) { cupdlpx_result_t *results = (cupdlpx_result_t *)safe_calloc(1, sizeof(cupdlpx_result_t)); @@ -1045,6 +1045,11 @@ static cupdlpx_result_t *create_result_from_state(pdhg_solver_state_t *state, co results->termination_reason = state->termination_reason; results->feasibility_polishing_time = state->feasibility_polishing_time; results->feasibility_iteration = state->feasibility_iteration; + if (presolve_stats != NULL) { + results->presolve_stats = *presolve_stats; + } else { + memset(&(results->presolve_stats), 0, sizeof(PresolveStats)); + } return results; } From 34eb3cd370e940ebc728c3b0f18843429f4bd3ee Mon Sep 17 00:00:00 2001 From: Zedong Peng Date: Mon, 22 Dec 2025 18:58:15 -0500 Subject: [PATCH 3/4] comment out presolve stats --- include/cupdlpx_types.h | 2 +- src/cli.c | 30 +++++++++++++++--------------- src/presolve.c | 9 ++++----- src/solver.cu | 16 ++++++++-------- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/include/cupdlpx_types.h b/include/cupdlpx_types.h index a52793a..b2cb101 100644 --- a/include/cupdlpx_types.h +++ b/include/cupdlpx_types.h @@ -115,7 +115,7 @@ extern "C" double cumulative_time_sec; double presolve_time; int presolve_status; - PresolveStats presolve_stats; + // PresolveStats presolve_stats; double absolute_primal_residual; double relative_primal_residual; diff --git a/src/cli.c b/src/cli.c index 398e299..690a9ff 100644 --- a/src/cli.c +++ b/src/cli.c @@ -125,22 +125,22 @@ void save_solver_summary(const cupdlpx_result_t *result, const char *output_dir, fprintf(outfile, "Reduced Columns: %d\n", result->num_reduced_variables); fprintf(outfile, "Reduced Nonzeros: %d\n", result->num_reduced_nonzeros); - if (result->presolve_stats.n_cols_original > 0) { - fprintf(outfile, "NNZ Removed Trivial: %d\n", result->presolve_stats.nnz_removed_trivial); - fprintf(outfile, "NNZ Removed Fast: %d\n", result->presolve_stats.nnz_removed_fast); - fprintf(outfile, "NNZ Removed Primal Propagation: %d\n", result->presolve_stats.nnz_removed_primal_propagation); - fprintf(outfile, "NNZ Removed Parallel Rows: %d\n", result->presolve_stats.nnz_removed_parallel_rows); - fprintf(outfile, "NNZ Removed Parallel Cols: %d\n", result->presolve_stats.nnz_removed_parallel_cols); + // if (result->presolve_stats.n_cols_original > 0) { + // fprintf(outfile, "NNZ Removed Trivial: %d\n", result->presolve_stats.nnz_removed_trivial); + // fprintf(outfile, "NNZ Removed Fast: %d\n", result->presolve_stats.nnz_removed_fast); + // fprintf(outfile, "NNZ Removed Primal Propagation: %d\n", result->presolve_stats.nnz_removed_primal_propagation); + // fprintf(outfile, "NNZ Removed Parallel Rows: %d\n", result->presolve_stats.nnz_removed_parallel_rows); + // fprintf(outfile, "NNZ Removed Parallel Cols: %d\n", result->presolve_stats.nnz_removed_parallel_cols); - fprintf(outfile, "Presolve Time Init (sec): %e\n", result->presolve_stats.time_init); - fprintf(outfile, "Presolve Time Run (sec): %e\n", result->presolve_stats.time_presolve); - fprintf(outfile, "Presolve Time Fast (sec): %e\n", result->presolve_stats.time_fast_reductions); - fprintf(outfile, "Presolve Time Medium (sec): %e\n", result->presolve_stats.time_medium_reductions); - fprintf(outfile, "Presolve Time Primal Proppagation (sec): %e\n", result->presolve_stats.time_primal_propagation); - fprintf(outfile, "Presolve Time Parallel Rows (sec): %e\n", result->presolve_stats.time_parallel_rows); - fprintf(outfile, "Presolve Time Parallel Cols (sec): %e\n", result->presolve_stats.time_parallel_cols); - fprintf(outfile, "Postsolve Time (sec): %e\n", result->presolve_stats.time_postsolve); - } + // fprintf(outfile, "Presolve Time Init (sec): %e\n", result->presolve_stats.time_init); + // fprintf(outfile, "Presolve Time Run (sec): %e\n", result->presolve_stats.time_presolve); + // fprintf(outfile, "Presolve Time Fast (sec): %e\n", result->presolve_stats.time_fast_reductions); + // fprintf(outfile, "Presolve Time Medium (sec): %e\n", result->presolve_stats.time_medium_reductions); + // fprintf(outfile, "Presolve Time Primal Proppagation (sec): %e\n", result->presolve_stats.time_primal_propagation); + // fprintf(outfile, "Presolve Time Parallel Rows (sec): %e\n", result->presolve_stats.time_parallel_rows); + // fprintf(outfile, "Presolve Time Parallel Cols (sec): %e\n", result->presolve_stats.time_parallel_cols); + // fprintf(outfile, "Postsolve Time (sec): %e\n", result->presolve_stats.time_postsolve); + // } } if (result->feasibility_polishing_time > 0.0) { diff --git a/src/presolve.c b/src/presolve.c index bffe68b..d2f0e0e 100644 --- a/src/presolve.c +++ b/src/presolve.c @@ -148,7 +148,7 @@ cupdlpx_result_t *create_result_from_presolve(const cupdlpx_presolve_info_t *inf result->num_reduced_nonzeros = info->presolver->reduced_prob->nnz; result->presolve_status = info->presolve_status; result->presolve_time = info->presolve_time; - result->presolve_stats = *(info->presolver->stats); + // result->presolve_stats = *(info->presolver->stats); // TODO: Verify if setting solution pointers to NULL affects Python/Julia bindings. if (result->num_variables > 0) { @@ -186,10 +186,9 @@ void pslp_postsolve(cupdlpx_presolve_info_t *info, memcpy(result->reduced_cost, info->presolver->sol->z, original_prob->num_variables * sizeof(double)); result->primal_objective_value = info->presolver->sol->obj; result->presolve_time = info->presolve_time; - // result->presolve_stats = *(info->presolver->stats); - if (info->presolver->stats != NULL) { - result->presolve_stats = *(info->presolver->stats); - } + // if (info->presolver->stats != NULL) { + // result->presolve_stats = *(info->presolver->stats); + // } } void cupdlpx_presolve_info_free(cupdlpx_presolve_info_t *info) diff --git a/src/solver.cu b/src/solver.cu index 39889c4..0e58dbf 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -66,7 +66,7 @@ static void compute_next_pdhg_dual_solution(pdhg_solver_state_t *state); static void halpern_update(pdhg_solver_state_t *state, double reflection_coefficient); 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, PresolveStats *presolve_stats); +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 @@ -187,7 +187,7 @@ cupdlpx_result_t *optimize(const pdhg_parameters_t *params, feasibility_polish(params, state); } - cupdlpx_result_t *result = create_result_from_state(state, original_problem, presolve_info->presolver->stats); + cupdlpx_result_t *result = create_result_from_state(state, original_problem); if (params->presolve && presolve_info) { @@ -982,7 +982,7 @@ void rescale_info_free(rescale_info_t *info) free(info); } -static cupdlpx_result_t *create_result_from_state(pdhg_solver_state_t *state, const lp_problem_t *original_problem, PresolveStats *presolve_stats) +static cupdlpx_result_t *create_result_from_state(pdhg_solver_state_t *state, const lp_problem_t *original_problem) { cupdlpx_result_t *results = (cupdlpx_result_t *)safe_calloc(1, sizeof(cupdlpx_result_t)); @@ -1045,11 +1045,11 @@ static cupdlpx_result_t *create_result_from_state(pdhg_solver_state_t *state, co results->termination_reason = state->termination_reason; results->feasibility_polishing_time = state->feasibility_polishing_time; results->feasibility_iteration = state->feasibility_iteration; - if (presolve_stats != NULL) { - results->presolve_stats = *presolve_stats; - } else { - memset(&(results->presolve_stats), 0, sizeof(PresolveStats)); - } + // if (presolve_stats != NULL) { + // results->presolve_stats = *presolve_stats; + // } else { + // memset(&(results->presolve_stats), 0, sizeof(PresolveStats)); + // } return results; } From b824e2e47c7675a32803e401a96d15d44ce1555b Mon Sep 17 00:00:00 2001 From: Zedong Peng Date: Mon, 22 Dec 2025 19:05:09 -0500 Subject: [PATCH 4/4] release v0.2.1 --- CMakeLists.txt | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5e9700..edf3d9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ project(cupdlpx LANGUAGES C CXX CUDA) set(CUPDLPX_VERSION_MAJOR 0) set(CUPDLPX_VERSION_MINOR 2) -set(CUPDLPX_VERSION_PATCH 0) +set(CUPDLPX_VERSION_PATCH 1) set(CUPDLPX_VERSION "${CUPDLPX_VERSION_MAJOR}.${CUPDLPX_VERSION_MINOR}.${CUPDLPX_VERSION_PATCH}") add_compile_definitions(CUPDLPX_VERSION="${CUPDLPX_VERSION}") diff --git a/pyproject.toml b/pyproject.toml index ea5c603..47a9d8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build" [project] name = "cupdlpx" -version = "0.2.0" +version = "0.2.1" description = "Python bindings for cuPDLPx (GPU-accelerated first-order LP solver)" readme = "README.md" license = { text = "Apache-2.0" }