From 910539ac64208f8fa70cf4ce62ab03b66d5d0878 Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Tue, 25 Nov 2025 17:48:53 -0500 Subject: [PATCH 01/17] Added infinity norm as a setting --- .gitignore | 3 ++ include/cupdlpx_types.h | 1 + internal/internal_types.h | 1 + python/cupdlpx/PDLP.py | 4 ++- python_bindings/_core_bindings.cpp | 6 ++++ src/solver.cu | 3 ++ src/utils.cu | 57 +++++++++++++++++++++++++----- test/test.sh | 0 8 files changed, 66 insertions(+), 9 deletions(-) mode change 100644 => 100755 test/test.sh diff --git a/.gitignore b/.gitignore index cd9fb58..866c33f 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ *.su *.idb *.pdb +Testing/* # Kernel Module Compile Results *.mod* @@ -60,3 +61,5 @@ test/* /.vscode /.venv /_b +*.whl +*.pyc diff --git a/include/cupdlpx_types.h b/include/cupdlpx_types.h index 23033bf..6638b06 100644 --- a/include/cupdlpx_types.h +++ b/include/cupdlpx_types.h @@ -89,6 +89,7 @@ extern "C" restart_parameters_t restart_params; double reflection_coefficient; bool feasibility_polishing; + bool use_linf_norm; } pdhg_parameters_t; typedef struct diff --git a/internal/internal_types.h b/internal/internal_types.h index 1f42582..b2f6e39 100644 --- a/internal/internal_types.h +++ b/internal/internal_types.h @@ -103,6 +103,7 @@ typedef struct double initial_fixed_point_error; double last_trial_fixed_point_error; int inner_count; + bool use_linf_norm; cusparseHandle_t sparse_handle; cublasHandle_t blas_handle; diff --git a/python/cupdlpx/PDLP.py b/python/cupdlpx/PDLP.py index cc2733d..50a261b 100644 --- a/python/cupdlpx/PDLP.py +++ b/python/cupdlpx/PDLP.py @@ -52,5 +52,7 @@ "ReflectionCoeff": "reflection_coefficient", # feasibility polishing "FeasibilityPolishing": "feasibility_polishing", - "FeasibilityPolishingTol": "eps_feas_polish_relative" + "FeasibilityPolishingTol": "eps_feas_polish_relative", + # termination criteria norm + "UseLInfNorm": "use_linf_norm" } \ No newline at end of file diff --git a/python_bindings/_core_bindings.cpp b/python_bindings/_core_bindings.cpp index 841b598..7be0c1d 100644 --- a/python_bindings/_core_bindings.cpp +++ b/python_bindings/_core_bindings.cpp @@ -240,6 +240,9 @@ static py::dict get_default_params_py() d["feasibility_polishing"] = p.feasibility_polishing; d["eps_feas_polish_relative"] = p.termination_criteria.eps_feas_polish_relative; + // Termination criteria norm + d["use_linf_norm"] = p.use_linf_norm; + return d; } @@ -288,6 +291,9 @@ static void parse_params_from_python(py::object params_obj, pdhg_parameters_t *p // Feasibility Polishing getb("feasibility_polishing", p->feasibility_polishing); getf("eps_feas_polish_relative", p->termination_criteria.eps_feas_polish_relative); + + // Termination criteria norm + getb("use_linf_norm", p->use_linf_norm); } // view of matrix from Python diff --git a/src/solver.cu b/src/solver.cu index a9e5f2b..bb9b69e 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -101,6 +101,8 @@ cupdlpx_result_t *optimize(const pdhg_parameters_t *params, pdhg_solver_state_t *state = initialize_solver_state(original_problem, rescale_info); + state->use_linf_norm = params ? params->use_linf_norm : false; + rescale_info_free(rescale_info); initialize_step_size_and_primal_weight(state, params); clock_t start_time = clock(); @@ -961,6 +963,7 @@ void set_default_parameters(pdhg_parameters_t *params) params->verbose = false; params->termination_evaluation_frequency = 200; params->feasibility_polishing = false; + params->use_linf_norm = false; params->reflection_coefficient = 1.0; params->termination_criteria.eps_optimal_relative = 1e-4; diff --git a/src/utils.cu b/src/utils.cu index 172e731..712b638 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -598,13 +598,36 @@ void compute_residual(pdhg_solver_state_t *state) state->constraint_upper_bound_finite_val, state->num_constraints, state->num_variables); - CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, - state->primal_residual, 1, - &state->absolute_primal_residual)); + if (state->use_linf_norm) { + state->absolute_primal_residual = get_vector_inf_norm(state->blas_handle, + state->num_constraints, state->primal_residual); + } else { + CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, + state->primal_residual, 1, + &state->absolute_primal_residual)); + } + + // CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, + // state->primal_residual, 1, + // &state->absolute_primal_residual)); + + // state->absolute_primal_residual = get_vector_inf_norm( + // state->blas_handle, state->num_constraints, state->primal_residual); state->absolute_primal_residual /= state->constraint_bound_rescaling; - CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_variables, - state->dual_residual, 1, - &state->absolute_dual_residual)); + + if (state->use_linf_norm) { + state->absolute_dual_residual = get_vector_inf_norm(state->blas_handle, + state->num_variables, state->dual_residual); + } else { + CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_variables, + state->dual_residual, 1, + &state->absolute_dual_residual)); + } + // CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_variables, + // state->dual_residual, 1, + // &state->absolute_dual_residual)); + // state->absolute_dual_residual = get_vector_inf_norm( + // state->blas_handle, state->num_variables, state->dual_residual); state->absolute_dual_residual /= state->objective_vector_rescaling; CUBLAS_CHECK(cublasDdot( @@ -1121,7 +1144,16 @@ void compute_primal_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_ state->constraint_upper_bound, state->constraint_rescaling, state->num_constraints); - CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, state->primal_residual, 1, &state->absolute_primal_residual)); + if (state->use_linf_norm) { + state->absolute_primal_residual = get_vector_inf_norm(state->blas_handle, + state->num_constraints, state->primal_residual); + } else { + CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, + state->primal_residual, 1, &state->absolute_primal_residual)); + } + // CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, state->primal_residual, 1, &state->absolute_primal_residual)); + // state->absolute_primal_residual = get_vector_inf_norm( + // state->blas_handle, state->num_constraints, state->primal_residual); state->absolute_primal_residual /= state->constraint_bound_rescaling; state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); @@ -1148,7 +1180,16 @@ void compute_dual_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_so state->num_variables, state->num_constraints ); - CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_variables, state->dual_residual, 1, &state->absolute_dual_residual)); + if (state->use_linf_norm) { + state->absolute_dual_residual = get_vector_inf_norm(state->blas_handle, + state->num_variables, state->dual_residual); + } else { + CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_variables, + state->dual_residual, 1, &state->absolute_dual_residual)); + } + // CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_variables, state->dual_residual, 1, &state->absolute_dual_residual)); + // state->absolute_dual_residual = get_vector_inf_norm( + // state->blas_handle, state->num_variables, state->dual_residual); state->absolute_dual_residual /= state->objective_vector_rescaling; state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm); diff --git a/test/test.sh b/test/test.sh old mode 100644 new mode 100755 From e10691cba789e78da88086b0a8112563b9aad82d Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Tue, 25 Nov 2025 17:57:02 -0500 Subject: [PATCH 02/17] Included the infinity norm description in the python README --- python/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/python/README.md b/python/README.md index 50c8fc9..73dc78a 100644 --- a/python/README.md +++ b/python/README.md @@ -141,6 +141,7 @@ Below is a list of commonly used parameters, their internal keys, and descriptio | `IterationLimit` | `iteration_limit` | int | `2147483647` | Maximum number of iterations. | | `OutputFlag`, `LogToConsole` | `verbose` | bool | `False` | Enable (`True`) or disable (`False`) console logging output. | | `TermCheckFreq` | `termination_evaluation_frequency` | int | `200` | Frequency (in iterations) at which termination conditions are evaluated. | +| `UseLInfNorm` | `use_linf_norm` | bool | `False` | Whether to use the infinity norm for the termination criteria or the L2 norm. | | `OptimalityTol` | `eps_optimal_relative` | float | `1e-4` | Relative tolerance for optimality gap. Solver stops if the relative primal-dual gap ≤ this value. | | `FeasibilityTol` | `eps_feasible_relative` | float | `1e-4` | Relative feasibility tolerance for primal/dual residuals. | | `InfeasibleTol` | `eps_infeasible` | float | `1e-10` | Threshold for declaring infeasibility. | From bb1c3bb587748471d8ff305705904885205323f5 Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Tue, 25 Nov 2025 18:27:56 -0500 Subject: [PATCH 03/17] Removed commented out code in utils.cu --- src/utils.cu | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/utils.cu b/src/utils.cu index 712b638..0ebd925 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -607,12 +607,6 @@ void compute_residual(pdhg_solver_state_t *state) &state->absolute_primal_residual)); } - // CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, - // state->primal_residual, 1, - // &state->absolute_primal_residual)); - - // state->absolute_primal_residual = get_vector_inf_norm( - // state->blas_handle, state->num_constraints, state->primal_residual); state->absolute_primal_residual /= state->constraint_bound_rescaling; if (state->use_linf_norm) { @@ -623,11 +617,7 @@ void compute_residual(pdhg_solver_state_t *state) state->dual_residual, 1, &state->absolute_dual_residual)); } - // CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_variables, - // state->dual_residual, 1, - // &state->absolute_dual_residual)); - // state->absolute_dual_residual = get_vector_inf_norm( - // state->blas_handle, state->num_variables, state->dual_residual); + state->absolute_dual_residual /= state->objective_vector_rescaling; CUBLAS_CHECK(cublasDdot( @@ -1151,9 +1141,7 @@ void compute_primal_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_ CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, state->primal_residual, 1, &state->absolute_primal_residual)); } - // CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, state->primal_residual, 1, &state->absolute_primal_residual)); - // state->absolute_primal_residual = get_vector_inf_norm( - // state->blas_handle, state->num_constraints, state->primal_residual); + state->absolute_primal_residual /= state->constraint_bound_rescaling; state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); @@ -1187,9 +1175,7 @@ void compute_dual_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_so CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_variables, state->dual_residual, 1, &state->absolute_dual_residual)); } - // CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_variables, state->dual_residual, 1, &state->absolute_dual_residual)); - // state->absolute_dual_residual = get_vector_inf_norm( - // state->blas_handle, state->num_variables, state->dual_residual); + state->absolute_dual_residual /= state->objective_vector_rescaling; state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm); From 6020267533fdb2aeb25d1ae971207605d24e2e36 Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Tue, 25 Nov 2025 19:53:37 -0500 Subject: [PATCH 04/17] Cleaning and adding unscalled termination criteria --- README.md | 2 ++ include/cupdlpx_types.h | 1 + internal/internal_types.h | 1 + python/README.md | 1 + python/cupdlpx/PDLP.py | 5 +++-- src/cli.c | 2 ++ src/solver.cu | 2 ++ src/utils.cu | 15 ++++++++++++--- 8 files changed, 24 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index afe8864..2ec8d67 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ The solver is invoked with the following syntax, specifying an input file and an | `-v`, `--verbose` | `flag` | Enable verbose logging. | `false` | | `--time_limit` | `double` | Time limit in seconds. | `3600.0` | | `--iter_limit` | `int` | Iteration limit. | `2147483647` | +| `--absolute_termination` | `flag` | Use unscalled termination criteria | `false` | +| `--linf_norm` | `flag` | Use infinity norm in termination criteria | `false` | | `--eps_opt` | `double` | Relative optimality tolerance. | `1e-4` | | `--eps_feas` | `double` | Relative feasibility tolerance. | `1e-4` | | `--eps_infeas_detect` | `double` | Infeasibility detection tolerance. | `1e-10` | diff --git a/include/cupdlpx_types.h b/include/cupdlpx_types.h index 6638b06..6486a50 100644 --- a/include/cupdlpx_types.h +++ b/include/cupdlpx_types.h @@ -90,6 +90,7 @@ extern "C" double reflection_coefficient; bool feasibility_polishing; bool use_linf_norm; + bool use_absolute_termination; } pdhg_parameters_t; typedef struct diff --git a/internal/internal_types.h b/internal/internal_types.h index b2f6e39..64de9f7 100644 --- a/internal/internal_types.h +++ b/internal/internal_types.h @@ -104,6 +104,7 @@ typedef struct double last_trial_fixed_point_error; int inner_count; bool use_linf_norm; + bool use_absolute_termination; cusparseHandle_t sparse_handle; cublasHandle_t blas_handle; diff --git a/python/README.md b/python/README.md index 73dc78a..fa83c6b 100644 --- a/python/README.md +++ b/python/README.md @@ -142,6 +142,7 @@ Below is a list of commonly used parameters, their internal keys, and descriptio | `OutputFlag`, `LogToConsole` | `verbose` | bool | `False` | Enable (`True`) or disable (`False`) console logging output. | | `TermCheckFreq` | `termination_evaluation_frequency` | int | `200` | Frequency (in iterations) at which termination conditions are evaluated. | | `UseLInfNorm` | `use_linf_norm` | bool | `False` | Whether to use the infinity norm for the termination criteria or the L2 norm. | +| `UseAbsoluteTermination` | `use_absolute_termination` | bool | `False` | Whether to use the unscalled termination criteria or not. | | `OptimalityTol` | `eps_optimal_relative` | float | `1e-4` | Relative tolerance for optimality gap. Solver stops if the relative primal-dual gap ≤ this value. | | `FeasibilityTol` | `eps_feasible_relative` | float | `1e-4` | Relative feasibility tolerance for primal/dual residuals. | | `InfeasibleTol` | `eps_infeasible` | float | `1e-10` | Threshold for declaring infeasibility. | diff --git a/python/cupdlpx/PDLP.py b/python/cupdlpx/PDLP.py index 50a261b..04ac872 100644 --- a/python/cupdlpx/PDLP.py +++ b/python/cupdlpx/PDLP.py @@ -53,6 +53,7 @@ # feasibility polishing "FeasibilityPolishing": "feasibility_polishing", "FeasibilityPolishingTol": "eps_feas_polish_relative", - # termination criteria norm - "UseLInfNorm": "use_linf_norm" + # termination criteria + "UseLInfNorm": "use_linf_norm", + "UseAbsoluteTermination": "use_absolute_termination" } \ No newline at end of file diff --git a/src/cli.c b/src/cli.c index b9588ad..6e07e9a 100644 --- a/src/cli.c +++ b/src/cli.c @@ -191,6 +191,8 @@ int main(int argc, char *argv[]) {"verbose", no_argument, 0, 'v'}, {"time_limit", required_argument, 0, 1001}, {"iter_limit", required_argument, 0, 1002}, + {"absolute_termination", no_argument, 0, 'f'} + {"linf_norm", no_argument, 0, 'f'} {"eps_opt", required_argument, 0, 1003}, {"eps_feas", required_argument, 0, 1004}, {"eps_infeas_detect", required_argument, 0, 1005}, diff --git a/src/solver.cu b/src/solver.cu index bb9b69e..7a859ae 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -102,6 +102,7 @@ cupdlpx_result_t *optimize(const pdhg_parameters_t *params, initialize_solver_state(original_problem, rescale_info); state->use_linf_norm = params ? params->use_linf_norm : false; + state->use_absolute_termination = params ? params->use_absolute_termination : false; rescale_info_free(rescale_info); initialize_step_size_and_primal_weight(state, params); @@ -964,6 +965,7 @@ void set_default_parameters(pdhg_parameters_t *params) params->termination_evaluation_frequency = 200; params->feasibility_polishing = false; params->use_linf_norm = false; + params->use_absolute_termination = false; params->reflection_coefficient = 1.0; params->termination_criteria.eps_optimal_relative = 1e-4; diff --git a/src/utils.cu b/src/utils.cu index 0ebd925..e936f17 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -220,9 +220,15 @@ const char *termination_reason_to_string(termination_reason_t reason) bool optimality_criteria_met(const pdhg_solver_state_t *state, double rel_opt_tol, double rel_feas_tol) { - return state->relative_dual_residual < rel_feas_tol && - state->relative_primal_residual < rel_feas_tol && - state->relative_objective_gap < rel_opt_tol; + if (state->use_absolute_termination) { + return state->absolute_primal_residual < rel_feas_tol && + state->absolute_dual_residual < rel_feas_tol && + state->objective_gap < rel_opt_tol; + } else { + return state->relative_dual_residual < rel_feas_tol && + state->relative_primal_residual < rel_feas_tol && + state->relative_objective_gap < rel_opt_tol; + } } bool primal_infeasibility_criteria_met(const pdhg_solver_state_t *state, @@ -380,6 +386,9 @@ void pdhg_final_log(const pdhg_solver_state_t *state, bool verbose, printf(" Dual obj : %.10g\n", state->dual_objective_value); printf(" Primal infeas : %.3e\n", state->relative_primal_residual); printf(" Dual infeas : %.3e\n", state->relative_dual_residual); + printf(" abs_prim_res : %.3e\n", state->absolute_primal_residual); + printf(" abs_dual_res : %.3e\n", state->absolute_dual_residual); + printf(" objective_gap : %.3e\n", state->objective_gap); } void display_iteration_stats(const pdhg_solver_state_t *state, bool verbose) From 3b125836ac02f950acf9a986b3e73cabc83d440f Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Tue, 25 Nov 2025 19:55:50 -0500 Subject: [PATCH 05/17] Minor codding error fix --- src/cli.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli.c b/src/cli.c index 6e07e9a..5b4c2dc 100644 --- a/src/cli.c +++ b/src/cli.c @@ -191,8 +191,8 @@ int main(int argc, char *argv[]) {"verbose", no_argument, 0, 'v'}, {"time_limit", required_argument, 0, 1001}, {"iter_limit", required_argument, 0, 1002}, - {"absolute_termination", no_argument, 0, 'f'} - {"linf_norm", no_argument, 0, 'f'} + {"absolute_termination", no_argument, 0, 'f'}, + {"linf_norm", no_argument, 0, 'f'}, {"eps_opt", required_argument, 0, 1003}, {"eps_feas", required_argument, 0, 1004}, {"eps_infeas_detect", required_argument, 0, 1005}, From 289321fedd9e91336bea296e00f0d6955cd6826b Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Tue, 25 Nov 2025 20:49:26 -0500 Subject: [PATCH 06/17] Fixed bindings for absolute termination --- python_bindings/_core_bindings.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python_bindings/_core_bindings.cpp b/python_bindings/_core_bindings.cpp index 7be0c1d..2c054fe 100644 --- a/python_bindings/_core_bindings.cpp +++ b/python_bindings/_core_bindings.cpp @@ -242,6 +242,7 @@ static py::dict get_default_params_py() // Termination criteria norm d["use_linf_norm"] = p.use_linf_norm; + d["use_absolute_termination"] = p.use_absolute_termination; return d; } @@ -294,6 +295,7 @@ static void parse_params_from_python(py::object params_obj, pdhg_parameters_t *p // Termination criteria norm getb("use_linf_norm", p->use_linf_norm); + getb("use_absolute_termination", p->use_absolute_termination); } // view of matrix from Python From 784b41935e2752c2709cee102dae83ac060590d9 Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Wed, 26 Nov 2025 08:00:27 -0500 Subject: [PATCH 07/17] Removed the printing of the absolute residual and gap in the final log --- src/utils.cu | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/utils.cu b/src/utils.cu index e936f17..a3a528e 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -386,9 +386,6 @@ void pdhg_final_log(const pdhg_solver_state_t *state, bool verbose, printf(" Dual obj : %.10g\n", state->dual_objective_value); printf(" Primal infeas : %.3e\n", state->relative_primal_residual); printf(" Dual infeas : %.3e\n", state->relative_dual_residual); - printf(" abs_prim_res : %.3e\n", state->absolute_primal_residual); - printf(" abs_dual_res : %.3e\n", state->absolute_dual_residual); - printf(" objective_gap : %.3e\n", state->objective_gap); } void display_iteration_stats(const pdhg_solver_state_t *state, bool verbose) From d2caadfb575693abff10bc8ada5c6118fde2bd3d Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Wed, 26 Nov 2025 10:29:11 -0500 Subject: [PATCH 08/17] Removed the use_absolute_termination --- README.md | 1 - include/cupdlpx_types.h | 1 - internal/internal_types.h | 1 - python/README.md | 1 - python/cupdlpx/PDLP.py | 1 - src/cli.c | 1 - src/solver.cu | 2 -- src/utils.cu | 12 +++--------- 8 files changed, 3 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 2ec8d67..58aeba5 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,6 @@ The solver is invoked with the following syntax, specifying an input file and an | `-v`, `--verbose` | `flag` | Enable verbose logging. | `false` | | `--time_limit` | `double` | Time limit in seconds. | `3600.0` | | `--iter_limit` | `int` | Iteration limit. | `2147483647` | -| `--absolute_termination` | `flag` | Use unscalled termination criteria | `false` | | `--linf_norm` | `flag` | Use infinity norm in termination criteria | `false` | | `--eps_opt` | `double` | Relative optimality tolerance. | `1e-4` | | `--eps_feas` | `double` | Relative feasibility tolerance. | `1e-4` | diff --git a/include/cupdlpx_types.h b/include/cupdlpx_types.h index 6486a50..6638b06 100644 --- a/include/cupdlpx_types.h +++ b/include/cupdlpx_types.h @@ -90,7 +90,6 @@ extern "C" double reflection_coefficient; bool feasibility_polishing; bool use_linf_norm; - bool use_absolute_termination; } pdhg_parameters_t; typedef struct diff --git a/internal/internal_types.h b/internal/internal_types.h index 64de9f7..b2f6e39 100644 --- a/internal/internal_types.h +++ b/internal/internal_types.h @@ -104,7 +104,6 @@ typedef struct double last_trial_fixed_point_error; int inner_count; bool use_linf_norm; - bool use_absolute_termination; cusparseHandle_t sparse_handle; cublasHandle_t blas_handle; diff --git a/python/README.md b/python/README.md index fa83c6b..73dc78a 100644 --- a/python/README.md +++ b/python/README.md @@ -142,7 +142,6 @@ Below is a list of commonly used parameters, their internal keys, and descriptio | `OutputFlag`, `LogToConsole` | `verbose` | bool | `False` | Enable (`True`) or disable (`False`) console logging output. | | `TermCheckFreq` | `termination_evaluation_frequency` | int | `200` | Frequency (in iterations) at which termination conditions are evaluated. | | `UseLInfNorm` | `use_linf_norm` | bool | `False` | Whether to use the infinity norm for the termination criteria or the L2 norm. | -| `UseAbsoluteTermination` | `use_absolute_termination` | bool | `False` | Whether to use the unscalled termination criteria or not. | | `OptimalityTol` | `eps_optimal_relative` | float | `1e-4` | Relative tolerance for optimality gap. Solver stops if the relative primal-dual gap ≤ this value. | | `FeasibilityTol` | `eps_feasible_relative` | float | `1e-4` | Relative feasibility tolerance for primal/dual residuals. | | `InfeasibleTol` | `eps_infeasible` | float | `1e-10` | Threshold for declaring infeasibility. | diff --git a/python/cupdlpx/PDLP.py b/python/cupdlpx/PDLP.py index 04ac872..54efea1 100644 --- a/python/cupdlpx/PDLP.py +++ b/python/cupdlpx/PDLP.py @@ -55,5 +55,4 @@ "FeasibilityPolishingTol": "eps_feas_polish_relative", # termination criteria "UseLInfNorm": "use_linf_norm", - "UseAbsoluteTermination": "use_absolute_termination" } \ No newline at end of file diff --git a/src/cli.c b/src/cli.c index 5b4c2dc..d158684 100644 --- a/src/cli.c +++ b/src/cli.c @@ -191,7 +191,6 @@ int main(int argc, char *argv[]) {"verbose", no_argument, 0, 'v'}, {"time_limit", required_argument, 0, 1001}, {"iter_limit", required_argument, 0, 1002}, - {"absolute_termination", no_argument, 0, 'f'}, {"linf_norm", no_argument, 0, 'f'}, {"eps_opt", required_argument, 0, 1003}, {"eps_feas", required_argument, 0, 1004}, diff --git a/src/solver.cu b/src/solver.cu index 7a859ae..bb9b69e 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -102,7 +102,6 @@ cupdlpx_result_t *optimize(const pdhg_parameters_t *params, initialize_solver_state(original_problem, rescale_info); state->use_linf_norm = params ? params->use_linf_norm : false; - state->use_absolute_termination = params ? params->use_absolute_termination : false; rescale_info_free(rescale_info); initialize_step_size_and_primal_weight(state, params); @@ -965,7 +964,6 @@ void set_default_parameters(pdhg_parameters_t *params) params->termination_evaluation_frequency = 200; params->feasibility_polishing = false; params->use_linf_norm = false; - params->use_absolute_termination = false; params->reflection_coefficient = 1.0; params->termination_criteria.eps_optimal_relative = 1e-4; diff --git a/src/utils.cu b/src/utils.cu index a3a528e..0702f4b 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -220,15 +220,9 @@ const char *termination_reason_to_string(termination_reason_t reason) bool optimality_criteria_met(const pdhg_solver_state_t *state, double rel_opt_tol, double rel_feas_tol) { - if (state->use_absolute_termination) { - return state->absolute_primal_residual < rel_feas_tol && - state->absolute_dual_residual < rel_feas_tol && - state->objective_gap < rel_opt_tol; - } else { - return state->relative_dual_residual < rel_feas_tol && - state->relative_primal_residual < rel_feas_tol && - state->relative_objective_gap < rel_opt_tol; - } + return state->relative_dual_residual < rel_feas_tol && + state->relative_primal_residual < rel_feas_tol && + state->relative_objective_gap < rel_opt_tol; } bool primal_infeasibility_criteria_met(const pdhg_solver_state_t *state, From 07806108a8ad201ad294565caa666325fa2d33ec Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Wed, 26 Nov 2025 16:24:30 -0500 Subject: [PATCH 09/17] pushing --- internal/internal_types.h | 1 + python_bindings/_core_bindings.cpp | 2 - src/solver.cu | 4 ++ src/utils.cu | 67 +++++++++++++++++++++++++----- 4 files changed, 62 insertions(+), 12 deletions(-) diff --git a/internal/internal_types.h b/internal/internal_types.h index b2f6e39..f506f87 100644 --- a/internal/internal_types.h +++ b/internal/internal_types.h @@ -47,6 +47,7 @@ typedef struct int num_blocks_dual; int num_blocks_primal_dual; double objective_vector_norm; + double objective_vector_norm_inf; double constraint_bound_norm; double *constraint_lower_bound_finite_val; double *constraint_upper_bound_finite_val; diff --git a/python_bindings/_core_bindings.cpp b/python_bindings/_core_bindings.cpp index 2c054fe..7be0c1d 100644 --- a/python_bindings/_core_bindings.cpp +++ b/python_bindings/_core_bindings.cpp @@ -242,7 +242,6 @@ static py::dict get_default_params_py() // Termination criteria norm d["use_linf_norm"] = p.use_linf_norm; - d["use_absolute_termination"] = p.use_absolute_termination; return d; } @@ -295,7 +294,6 @@ static void parse_params_from_python(py::object params_obj, pdhg_parameters_t *p // Termination criteria norm getb("use_linf_norm", p->use_linf_norm); - getb("use_absolute_termination", p->use_absolute_termination); } // view of matrix from Python diff --git a/src/solver.cu b/src/solver.cu index bb9b69e..078bb4f 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -356,13 +356,17 @@ initialize_solver_state(const lp_problem_t *original_problem, free(temp_host); double sum_of_squares = 0.0; + double max_obj = 0.0; for (int i = 0; i < n_vars; ++i) { sum_of_squares += original_problem->objective_vector[i] * original_problem->objective_vector[i]; + double val = fabs(original_problem->objective_vector[i]); + if (val > max_obj) max_obj = val; } state->objective_vector_norm = sqrt(sum_of_squares); + state->objective_vector_norm_inf = max_obj; sum_of_squares = 0.0; diff --git a/src/utils.cu b/src/utils.cu index 0702f4b..7afda37 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -640,15 +640,44 @@ void compute_residual(pdhg_solver_state_t *state) state->objective_vector_rescaling) + state->objective_constant; - state->relative_primal_residual = - state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); - state->relative_dual_residual = - state->absolute_dual_residual / (1.0 + state->objective_vector_norm); + if (state->use_linf_norm) { + double ax_inf_norm = get_vector_inf_norm(state->blas_handle, + state->num_constraints, + state->primal_product); + state->relative_primal_residual = + state->absolute_primal_residual / (1.0 + ax_inf_norm); + + double aty_inf_norm = get_vector_inf_norm(state->blas_handle, + state->num_variables, + state->dual_product); + double dual_normalizer = fmax(state->objective_vector_norm_inf, aty_inf_norm); + state->relative_dual_residual = + state->absolute_dual_residual / (1.0 + dual_normalizer); + } else { + state->relative_primal_residual = + state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); + state->relative_dual_residual = + state->absolute_dual_residual / (1.0 + state->objective_vector_norm); + } + // state->relative_primal_residual = + // state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); + // state->relative_dual_residual = + // state->absolute_dual_residual / (1.0 + state->objective_vector_norm); state->objective_gap = fabs(state->primal_objective_value - state->dual_objective_value); - state->relative_objective_gap = - state->objective_gap / (1.0 + fabs(state->primal_objective_value) + - fabs(state->dual_objective_value)); + + if (state->use_linf_norm) { + double max_obj = fmax(fabs(state->primal_objective_value), + fabs(state->dual_objective_value)); + state->relative_objective_gap = state->objective_gap / (1.0 + max_obj); + } else { + state->relative_objective_gap = + state->objective_gap / (1.0 + fabs(state->primal_objective_value) + + fabs(state->dual_objective_value)); + } + // state->relative_objective_gap = + // state->objective_gap / (1.0 + fabs(state->primal_objective_value) + + // fabs(state->dual_objective_value)); } void compute_infeasibility_information(pdhg_solver_state_t *state) @@ -1139,11 +1168,20 @@ void compute_primal_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_ state->num_constraints, state->primal_residual); } else { CUBLAS_CHECK(cublasDnrm2_v2_64(state->blas_handle, state->num_constraints, - state->primal_residual, 1, &state->absolute_primal_residual)); + state->primal_residual, 1, + &state->absolute_primal_residual)); } state->absolute_primal_residual /= state->constraint_bound_rescaling; - state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); + + if (state->use_linf_norm) { + double ax_inf_norm = get_vector_inf_norm(state->blas_handle, + state->num_constraints, + state->primal_product); + state->relative_primal_residual = state->absolute_primal_residual / (1.0 + ax_inf_norm); + } else { + state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); + } CUBLAS_CHECK(cublasDdot(state->blas_handle, state->num_variables, ori_state->objective_vector, 1, state->pdhg_primal_solution, 1, &state->primal_objective_value)); state->primal_objective_value = state->primal_objective_value / (state->constraint_bound_rescaling * state->objective_vector_rescaling) + state->objective_constant; @@ -1177,7 +1215,16 @@ void compute_dual_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_so } state->absolute_dual_residual /= state->objective_vector_rescaling; - state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm); + + if (state->use_linf_norm) { + double aty_inf_norm = get_vector_inf_norm(state->blas_handle, + state->num_variables, + state->dual_product); + double dual_normalizer = fmax(state->objective_vector_norm_inf, aty_inf_norm); + state->relative_dual_residual = state->absolute_dual_residual / (1.0 + dual_normalizer); + } else { + state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm); + } double base_dual_objective; CUBLAS_CHECK(cublasDdot(state->blas_handle, state->num_variables, state->dual_slack, 1, ori_state->pdhg_primal_solution, 1, &base_dual_objective)); From 633e70bc154ea74bff1b345e12fc0e12cce56626 Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Wed, 26 Nov 2025 20:21:14 -0500 Subject: [PATCH 10/17] Removed old logic, just made it such that we change L2 norm to L-infinity norm --- internal/internal_types.h | 1 + src/solver.cu | 17 ++++++++++++---- src/utils.cu | 42 ++++++++------------------------------- 3 files changed, 22 insertions(+), 38 deletions(-) diff --git a/internal/internal_types.h b/internal/internal_types.h index f506f87..b612764 100644 --- a/internal/internal_types.h +++ b/internal/internal_types.h @@ -48,6 +48,7 @@ typedef struct int num_blocks_primal_dual; double objective_vector_norm; double objective_vector_norm_inf; + double constraint_bound_norm_inf; double constraint_bound_norm; double *constraint_lower_bound_finite_val; double *constraint_upper_bound_finite_val; diff --git a/src/solver.cu b/src/solver.cu index 078bb4f..6a88ccd 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -356,19 +356,22 @@ initialize_solver_state(const lp_problem_t *original_problem, free(temp_host); double sum_of_squares = 0.0; - double max_obj = 0.0; + double max_val = 0.0; + double val = 0.0 for (int i = 0; i < n_vars; ++i) { sum_of_squares += original_problem->objective_vector[i] * original_problem->objective_vector[i]; - double val = fabs(original_problem->objective_vector[i]); - if (val > max_obj) max_obj = val; + val = fabs(original_problem->objective_vector[i]); + if (val > max_obj) max_val = val; } state->objective_vector_norm = sqrt(sum_of_squares); - state->objective_vector_norm_inf = max_obj; + state->objective_vector_norm_inf = max_val; sum_of_squares = 0.0; + max_val = 0.0; + val = 0.0; for (int i = 0; i < n_cons; ++i) { @@ -378,15 +381,21 @@ initialize_solver_state(const lp_problem_t *original_problem, if (isfinite(lower) && (lower != upper)) { sum_of_squares += lower * lower; + val = fabs(lower); + if (val > max_obj) max_val = val; } if (isfinite(upper)) { sum_of_squares += upper * upper; + val = fabs(upper); + if (val > max_obj) max_val = val; } } state->constraint_bound_norm = sqrt(sum_of_squares); + state->constraint_bound_norm_inf = max_val; + state->num_blocks_primal = (state->num_variables + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; state->num_blocks_dual = diff --git a/src/utils.cu b/src/utils.cu index 7afda37..326a561 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -641,43 +641,24 @@ void compute_residual(pdhg_solver_state_t *state) state->objective_constant; if (state->use_linf_norm) { - double ax_inf_norm = get_vector_inf_norm(state->blas_handle, - state->num_constraints, - state->primal_product); state->relative_primal_residual = - state->absolute_primal_residual / (1.0 + ax_inf_norm); + state->absolute_primal_residual / (1.0 + state->constraint_bound_norm_inf); - double aty_inf_norm = get_vector_inf_norm(state->blas_handle, - state->num_variables, - state->dual_product); - double dual_normalizer = fmax(state->objective_vector_norm_inf, aty_inf_norm); state->relative_dual_residual = - state->absolute_dual_residual / (1.0 + dual_normalizer); + state->absolute_dual_residual / (1.0 + state->objective_vector_norm_inf); } else { state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm); } - // state->relative_primal_residual = - // state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); - // state->relative_dual_residual = - // state->absolute_dual_residual / (1.0 + state->objective_vector_norm); + state->objective_gap = fabs(state->primal_objective_value - state->dual_objective_value); - if (state->use_linf_norm) { - double max_obj = fmax(fabs(state->primal_objective_value), - fabs(state->dual_objective_value)); - state->relative_objective_gap = state->objective_gap / (1.0 + max_obj); - } else { - state->relative_objective_gap = - state->objective_gap / (1.0 + fabs(state->primal_objective_value) + - fabs(state->dual_objective_value)); - } - // state->relative_objective_gap = - // state->objective_gap / (1.0 + fabs(state->primal_objective_value) + - // fabs(state->dual_objective_value)); + state->relative_objective_gap = + state->objective_gap / (1.0 + fabs(state->primal_objective_value) + + fabs(state->dual_objective_value)); } void compute_infeasibility_information(pdhg_solver_state_t *state) @@ -1175,10 +1156,7 @@ void compute_primal_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_ state->absolute_primal_residual /= state->constraint_bound_rescaling; if (state->use_linf_norm) { - double ax_inf_norm = get_vector_inf_norm(state->blas_handle, - state->num_constraints, - state->primal_product); - state->relative_primal_residual = state->absolute_primal_residual / (1.0 + ax_inf_norm); + state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm_inf); } else { state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); } @@ -1217,11 +1195,7 @@ void compute_dual_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_so state->absolute_dual_residual /= state->objective_vector_rescaling; if (state->use_linf_norm) { - double aty_inf_norm = get_vector_inf_norm(state->blas_handle, - state->num_variables, - state->dual_product); - double dual_normalizer = fmax(state->objective_vector_norm_inf, aty_inf_norm); - state->relative_dual_residual = state->absolute_dual_residual / (1.0 + dual_normalizer); + state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm_inf); } else { state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm); } From 5876e883470437c8cd73906e3703b54f2cf3e810 Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Wed, 26 Nov 2025 23:06:16 -0500 Subject: [PATCH 11/17] Fixed small bugs --- src/solver.cu | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/solver.cu b/src/solver.cu index 6a88ccd..3afdcf8 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -357,14 +357,14 @@ initialize_solver_state(const lp_problem_t *original_problem, double sum_of_squares = 0.0; double max_val = 0.0; - double val = 0.0 + double val = 0.0; for (int i = 0; i < n_vars; ++i) { sum_of_squares += original_problem->objective_vector[i] * original_problem->objective_vector[i]; val = fabs(original_problem->objective_vector[i]); - if (val > max_obj) max_val = val; + if (val > max_val) max_val = val; } state->objective_vector_norm = sqrt(sum_of_squares); state->objective_vector_norm_inf = max_val; @@ -382,14 +382,14 @@ initialize_solver_state(const lp_problem_t *original_problem, { sum_of_squares += lower * lower; val = fabs(lower); - if (val > max_obj) max_val = val; + if (val > max_val) max_val = val; } if (isfinite(upper)) { sum_of_squares += upper * upper; val = fabs(upper); - if (val > max_obj) max_val = val; + if (val > max_val) max_val = val; } } From 77d9a7cd2579bdf4ad020371e33c04f7132e98f3 Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Thu, 18 Dec 2025 15:30:28 -0500 Subject: [PATCH 12/17] Updated norm API --- include/cupdlpx_types.h | 8 +++++++- internal/internal_types.h | 2 +- python/cupdlpx/PDLP.py | 6 +++++- python_bindings/_core_bindings.cpp | 11 +++++++++-- src/cli.c | 14 ++++++++++++++ src/solver.cu | 2 +- src/utils.cu | 16 +++++++++------- 7 files changed, 46 insertions(+), 13 deletions(-) diff --git a/include/cupdlpx_types.h b/include/cupdlpx_types.h index 5dd747c..2d86deb 100644 --- a/include/cupdlpx_types.h +++ b/include/cupdlpx_types.h @@ -35,6 +35,12 @@ extern "C" TERMINATION_REASON_FEAS_POLISH_SUCCESS } termination_reason_t; + typedef enum + { + NORM_TYPE_L2 = 0, + NORM_TYPE_L_INF = 1 + } norm_type_t; + typedef struct { int num_variables; @@ -91,7 +97,7 @@ extern "C" restart_parameters_t restart_params; double reflection_coefficient; bool feasibility_polishing; - bool use_linf_norm; + norm_type_t optimality_norm; } pdhg_parameters_t; typedef struct diff --git a/internal/internal_types.h b/internal/internal_types.h index b612764..0b7a394 100644 --- a/internal/internal_types.h +++ b/internal/internal_types.h @@ -105,7 +105,7 @@ typedef struct double initial_fixed_point_error; double last_trial_fixed_point_error; int inner_count; - bool use_linf_norm; + norm_type_t optimality_norm; cusparseHandle_t sparse_handle; cublasHandle_t blas_handle; diff --git a/python/cupdlpx/PDLP.py b/python/cupdlpx/PDLP.py index 85e2735..6d159d9 100644 --- a/python/cupdlpx/PDLP.py +++ b/python/cupdlpx/PDLP.py @@ -24,6 +24,10 @@ ITERATION_LIMIT = 4 UNSPECIFIED = -1 +# Norm types for optimality criteria +L2 = 0 +L_INF = 1 + # parameter name alias _PARAM_ALIAS = { @@ -54,7 +58,7 @@ "FeasibilityPolishing": "feasibility_polishing", "FeasibilityPolishingTol": "eps_feas_polish_relative", # termination criteria - "UseLInfNorm": "use_linf_norm", + "OptimalityNorm": "optimality_norm", # singular value estimation (power method) "SVMaxIter": "sv_max_iter", "SVTol": "sv_tol", diff --git a/python_bindings/_core_bindings.cpp b/python_bindings/_core_bindings.cpp index d9e7ab6..361fb1f 100644 --- a/python_bindings/_core_bindings.cpp +++ b/python_bindings/_core_bindings.cpp @@ -244,7 +244,7 @@ static py::dict get_default_params_py() d["eps_feas_polish_relative"] = p.termination_criteria.eps_feas_polish_relative; // Termination criteria norm - d["use_linf_norm"] = p.use_linf_norm; + d["optimality_norm"] = static_cast(p.optimality_norm); // power method for singular value estimation d["sv_max_iter"] = p.sv_max_iter; d["sv_tol"] = p.sv_tol; @@ -265,6 +265,13 @@ static void parse_params_from_python(py::object params_obj, pdhg_parameters_t *p { if (d.contains(k)) tgt = py::cast(d[k]); }; auto getb = [&](const char *k, bool &tgt) { if (d.contains(k)) tgt = py::cast(d[k]); }; + auto gete = [&](const char *k, norm_type_t &tgt) + { + if (d.contains(k)) { + int val = py::cast(d[k]); + tgt = static_cast(val); + } + }; // verbosity getb("verbose", p->verbose); @@ -299,7 +306,7 @@ static void parse_params_from_python(py::object params_obj, pdhg_parameters_t *p getf("eps_feas_polish_relative", p->termination_criteria.eps_feas_polish_relative); // Termination criteria norm - getb("use_linf_norm", p->use_linf_norm); + gete("optimality_norm", p->optimality_norm); // power method for singular value estimation geti("sv_max_iter", p->sv_max_iter); getf("sv_tol", p->sv_tol); diff --git a/src/cli.c b/src/cli.c index a746dc5..ffcb728 100644 --- a/src/cli.c +++ b/src/cli.c @@ -171,6 +171,8 @@ void print_usage(const char *prog_name) "Enable feasibility use feasibility polishing (default: false).\n"); fprintf(stderr, " --eps_feas_polish Relative feasibility " "polish tolerance (default: 1e-6).\n"); + fprintf(stderr, " --optimality_norm " + "Norm for optimality criteria: L2 (0) or L_INF (1) (default: L2).\n"); } int main(int argc, char *argv[]) @@ -196,6 +198,7 @@ int main(int argc, char *argv[]) {"sv_max_iter", required_argument, 0, 1011}, {"sv_tol", required_argument, 0, 1012}, {"eval_freq", required_argument, 0, 1013}, + {"optimality_norm", required_argument, 0, 1014}, {0, 0, 0, 0}}; int opt; @@ -251,6 +254,17 @@ int main(int argc, char *argv[]) case 1013: // --eval_freq params.termination_evaluation_frequency = atoi(optarg); break; + case 1014: // --optimality_norm + { + int norm_val = atoi(optarg); + if (norm_val == 0 || norm_val == 1) { + params.optimality_norm = (norm_type_t)norm_val; + } else { + fprintf(stderr, "Error: optimality_norm must be 0 (L2) or 1 (L_INF)\n"); + return 1; + } + } + break; case '?': // Unknown option return 1; } diff --git a/src/solver.cu b/src/solver.cu index d856e9f..e6f8e21 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -101,7 +101,7 @@ cupdlpx_result_t *optimize(const pdhg_parameters_t *params, pdhg_solver_state_t *state = initialize_solver_state(original_problem, rescale_info); - state->use_linf_norm = params ? params->use_linf_norm : false; + state->optimality_norm = params ? params->optimality_norm : NORM_TYPE_L2; rescale_info_free(rescale_info); initialize_step_size_and_primal_weight(state, params); diff --git a/src/utils.cu b/src/utils.cu index 6e0f0f6..62a59c6 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -346,6 +346,8 @@ void set_default_parameters(pdhg_parameters_t *params) params->restart_params.k_i = 0.01; params->restart_params.k_d = 0.0; params->restart_params.i_smooth = 0.3; + + params->optimality_norm = NORM_TYPE_L2; } #define PRINT_DIFF_INT(name, current, default_val) \ @@ -683,7 +685,7 @@ void compute_residual(pdhg_solver_state_t *state) state->constraint_upper_bound_finite_val, state->num_constraints, state->num_variables); - if (state->use_linf_norm) { + if (state->optimality_norm == NORM_TYPE_L_INF) { state->absolute_primal_residual = get_vector_inf_norm(state->blas_handle, state->num_constraints, state->primal_residual); } else { @@ -694,7 +696,7 @@ void compute_residual(pdhg_solver_state_t *state) state->absolute_primal_residual /= state->constraint_bound_rescaling; - if (state->use_linf_norm) { + if (state->optimality_norm == NORM_TYPE_L_INF) { state->absolute_dual_residual = get_vector_inf_norm(state->blas_handle, state->num_variables, state->dual_residual); } else { @@ -725,7 +727,7 @@ void compute_residual(pdhg_solver_state_t *state) state->objective_vector_rescaling) + state->objective_constant; - if (state->use_linf_norm) { + if (state->optimality_norm == NORM_TYPE_L_INF) { state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm_inf); @@ -1229,7 +1231,7 @@ void compute_primal_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_ state->constraint_upper_bound, state->constraint_rescaling, state->num_constraints); - if (state->use_linf_norm) { + if (state->optimality_norm == NORM_TYPE_L_INF) { state->absolute_primal_residual = get_vector_inf_norm(state->blas_handle, state->num_constraints, state->primal_residual); } else { @@ -1240,7 +1242,7 @@ void compute_primal_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_ state->absolute_primal_residual /= state->constraint_bound_rescaling; - if (state->use_linf_norm) { + if (state->optimality_norm == NORM_TYPE_L_INF) { state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm_inf); } else { state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); @@ -1269,7 +1271,7 @@ void compute_dual_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_so state->num_variables, state->num_constraints ); - if (state->use_linf_norm) { + if (state->optimality_norm == NORM_TYPE_L_INF) { state->absolute_dual_residual = get_vector_inf_norm(state->blas_handle, state->num_variables, state->dual_residual); } else { @@ -1279,7 +1281,7 @@ void compute_dual_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_so state->absolute_dual_residual /= state->objective_vector_rescaling; - if (state->use_linf_norm) { + if (state->optimality_norm == NORM_TYPE_L_INF) { state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm_inf); } else { state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm); From 05b73751bb9f3943518adb54f7905d1b63b6dfcb Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Thu, 18 Dec 2025 15:36:02 -0500 Subject: [PATCH 13/17] Updated Readme's to include new API --- README.md | 2 +- python/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index beea9d2..82e10cf 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ After building the project, the `./build/cupdlpx` binary can be invoked from the | `-v`, `--verbose` | `flag` | Enable verbose logging. | `false` | | `--time_limit` | `double` | Time limit in seconds. | `3600.0` | | `--iter_limit` | `int` | Iteration limit. | `2147483647` | -| `--linf_norm` | `flag` | Use infinity norm in termination criteria | `false` | +| `--optimality_norm` | `int` | Norm for optimality criteria: L2 (0) or L_INF (1) | `0` (L2) | | `--eps_opt` | `double` | Relative optimality tolerance. | `1e-4` | | `--eps_feas` | `double` | Relative feasibility tolerance. | `1e-4` | | `--eps_infeas_detect` | `double` | Infeasibility detection tolerance. | `1e-10` | diff --git a/python/README.md b/python/README.md index 751ceaf..a9e90cc 100644 --- a/python/README.md +++ b/python/README.md @@ -141,7 +141,7 @@ Below is a list of commonly used parameters, their internal keys, and descriptio | `IterationLimit` | `iteration_limit` | int | `2147483647` | Maximum number of iterations. | | `OutputFlag`, `LogToConsole` | `verbose` | bool | `False` | Enable (`True`) or disable (`False`) console logging output. | | `TermCheckFreq` | `termination_evaluation_frequency` | int | `200` | Frequency (in iterations) at which termination conditions are evaluated. | -| `UseLInfNorm` | `use_linf_norm` | bool | `False` | Whether to use the infinity norm for the termination criteria or the L2 norm. | +| `OptimalityNorm` | `optimality_norm` | int | `PDLP.L2` (0) | Norm for optimality criteria. Use `PDLP.L2` (0) for L2 norm or `PDLP.L_INF` (1) for infinity norm. | | `OptimalityTol` | `eps_optimal_relative` | float | `1e-4` | Relative tolerance for optimality gap. Solver stops if the relative primal-dual gap ≤ this value. | | `FeasibilityTol` | `eps_feasible_relative` | float | `1e-4` | Relative feasibility tolerance for primal/dual residuals. | | `InfeasibleTol` | `eps_infeasible` | float | `1e-10` | Threshold for declaring infeasibility. | From af492c2587ac2f7cabb4b208c23c2ee5303c89e4 Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Thu, 25 Dec 2025 13:07:04 -0500 Subject: [PATCH 14/17] API update --- README.md | 2 +- internal/internal_types.h | 2 -- python/README.md | 2 +- python/cupdlpx/PDLP.py | 4 --- python_bindings/_core_bindings.cpp | 31 +++++++++++++++--- src/cli.c | 19 +++++------ src/solver.cu | 52 +++++++++++++++++++----------- src/utils.cu | 29 ++++------------- 8 files changed, 78 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 82e10cf..964e248 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ After building the project, the `./build/cupdlpx` binary can be invoked from the | `-v`, `--verbose` | `flag` | Enable verbose logging. | `false` | | `--time_limit` | `double` | Time limit in seconds. | `3600.0` | | `--iter_limit` | `int` | Iteration limit. | `2147483647` | -| `--optimality_norm` | `int` | Norm for optimality criteria: L2 (0) or L_INF (1) | `0` (L2) | +| `--opt_norm` | `string` | Norm for optimality criteria: `l2` or `linf` | `l2` | | `--eps_opt` | `double` | Relative optimality tolerance. | `1e-4` | | `--eps_feas` | `double` | Relative feasibility tolerance. | `1e-4` | | `--eps_infeas_detect` | `double` | Infeasibility detection tolerance. | `1e-10` | diff --git a/internal/internal_types.h b/internal/internal_types.h index 0b7a394..9eef900 100644 --- a/internal/internal_types.h +++ b/internal/internal_types.h @@ -47,8 +47,6 @@ typedef struct int num_blocks_dual; int num_blocks_primal_dual; double objective_vector_norm; - double objective_vector_norm_inf; - double constraint_bound_norm_inf; double constraint_bound_norm; double *constraint_lower_bound_finite_val; double *constraint_upper_bound_finite_val; diff --git a/python/README.md b/python/README.md index a9e90cc..15448b4 100644 --- a/python/README.md +++ b/python/README.md @@ -141,7 +141,7 @@ Below is a list of commonly used parameters, their internal keys, and descriptio | `IterationLimit` | `iteration_limit` | int | `2147483647` | Maximum number of iterations. | | `OutputFlag`, `LogToConsole` | `verbose` | bool | `False` | Enable (`True`) or disable (`False`) console logging output. | | `TermCheckFreq` | `termination_evaluation_frequency` | int | `200` | Frequency (in iterations) at which termination conditions are evaluated. | -| `OptimalityNorm` | `optimality_norm` | int | `PDLP.L2` (0) | Norm for optimality criteria. Use `PDLP.L2` (0) for L2 norm or `PDLP.L_INF` (1) for infinity norm. | +| `OptimalityNorm` | `optimality_norm` | string | `"l2"` | Norm for optimality criteria. Use `"l2"` for L2 norm or `"linf"` for infinity norm. | | `OptimalityTol` | `eps_optimal_relative` | float | `1e-4` | Relative tolerance for optimality gap. Solver stops if the relative primal-dual gap ≤ this value. | | `FeasibilityTol` | `eps_feasible_relative` | float | `1e-4` | Relative feasibility tolerance for primal/dual residuals. | | `InfeasibleTol` | `eps_infeasible` | float | `1e-10` | Threshold for declaring infeasibility. | diff --git a/python/cupdlpx/PDLP.py b/python/cupdlpx/PDLP.py index 6d159d9..6fd3271 100644 --- a/python/cupdlpx/PDLP.py +++ b/python/cupdlpx/PDLP.py @@ -24,10 +24,6 @@ ITERATION_LIMIT = 4 UNSPECIFIED = -1 -# Norm types for optimality criteria -L2 = 0 -L_INF = 1 - # parameter name alias _PARAM_ALIAS = { diff --git a/python_bindings/_core_bindings.cpp b/python_bindings/_core_bindings.cpp index 361fb1f..7051dde 100644 --- a/python_bindings/_core_bindings.cpp +++ b/python_bindings/_core_bindings.cpp @@ -137,6 +137,21 @@ static const int32_t *get_index_ptr_i32(py::object obj, const char *name, throw std::invalid_argument(std::string(name) + " must be int32 or int64."); } +// helper function to convert norm string to enum +static norm_type_t parse_norm_string(const std::string& s) +{ + std::string lower = s; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + + if (lower == "l2") { + return NORM_TYPE_L2; + } else if (lower == "linf") { + return NORM_TYPE_L_INF; + } else { + throw std::invalid_argument("Unknown norm type: " + s + ". Use 'l2' or 'linf'."); + } +} + // ensure 1D array or None with expected length static void ensure_len_or_null(py::object obj, const char *name, int expect_len) { @@ -244,7 +259,7 @@ static py::dict get_default_params_py() d["eps_feas_polish_relative"] = p.termination_criteria.eps_feas_polish_relative; // Termination criteria norm - d["optimality_norm"] = static_cast(p.optimality_norm); + d["optimality_norm"] = (p.optimality_norm == NORM_TYPE_L_INF) ? "linf" : "l2"; // power method for singular value estimation d["sv_max_iter"] = p.sv_max_iter; d["sv_tol"] = p.sv_tol; @@ -265,11 +280,17 @@ static void parse_params_from_python(py::object params_obj, pdhg_parameters_t *p { if (d.contains(k)) tgt = py::cast(d[k]); }; auto getb = [&](const char *k, bool &tgt) { if (d.contains(k)) tgt = py::cast(d[k]); }; - auto gete = [&](const char *k, norm_type_t &tgt) + auto get_norm = [&](const char *k, norm_type_t &tgt) { if (d.contains(k)) { - int val = py::cast(d[k]); - tgt = static_cast(val); + py::object val = d[k]; + if (py::isinstance(val)) { + std::string sval = py::cast(val); + tgt = parse_norm_string(sval); + } + else { + throw std::invalid_argument("optimality_norm must be a string ('l2'/'linf')"); + } } }; @@ -306,7 +327,7 @@ static void parse_params_from_python(py::object params_obj, pdhg_parameters_t *p getf("eps_feas_polish_relative", p->termination_criteria.eps_feas_polish_relative); // Termination criteria norm - gete("optimality_norm", p->optimality_norm); + get_norm("optimality_norm", p->optimality_norm); // power method for singular value estimation geti("sv_max_iter", p->sv_max_iter); getf("sv_tol", p->sv_tol); diff --git a/src/cli.c b/src/cli.c index ffcb728..04c201b 100644 --- a/src/cli.c +++ b/src/cli.c @@ -171,8 +171,8 @@ void print_usage(const char *prog_name) "Enable feasibility use feasibility polishing (default: false).\n"); fprintf(stderr, " --eps_feas_polish Relative feasibility " "polish tolerance (default: 1e-6).\n"); - fprintf(stderr, " --optimality_norm " - "Norm for optimality criteria: L2 (0) or L_INF (1) (default: L2).\n"); + fprintf(stderr, " --opt_norm " + "Norm for optimality criteria: l2 or linf (default: l2).\n"); } int main(int argc, char *argv[]) @@ -185,7 +185,6 @@ int main(int argc, char *argv[]) {"verbose", no_argument, 0, 'v'}, {"time_limit", required_argument, 0, 1001}, {"iter_limit", required_argument, 0, 1002}, - {"linf_norm", no_argument, 0, 'f'}, {"eps_opt", required_argument, 0, 1003}, {"eps_feas", required_argument, 0, 1004}, {"eps_infeas_detect", required_argument, 0, 1005}, @@ -198,7 +197,7 @@ int main(int argc, char *argv[]) {"sv_max_iter", required_argument, 0, 1011}, {"sv_tol", required_argument, 0, 1012}, {"eval_freq", required_argument, 0, 1013}, - {"optimality_norm", required_argument, 0, 1014}, + {"opt_norm", required_argument, 0, 1014}, {0, 0, 0, 0}}; int opt; @@ -254,13 +253,15 @@ int main(int argc, char *argv[]) case 1013: // --eval_freq params.termination_evaluation_frequency = atoi(optarg); break; - case 1014: // --optimality_norm + case 1014: // --opt_norm { - int norm_val = atoi(optarg); - if (norm_val == 0 || norm_val == 1) { - params.optimality_norm = (norm_type_t)norm_val; + const char *norm_str = optarg; + if (strcmp(norm_str, "l2") == 0) { + params.optimality_norm = NORM_TYPE_L2; + } else if (strcmp(norm_str, "linf") == 0) { + params.optimality_norm = NORM_TYPE_L_INF; } else { - fprintf(stderr, "Error: optimality_norm must be 0 (L2) or 1 (L_INF)\n"); + fprintf(stderr, "Error: opt_norm must be 'l2' or 'linf'\n"); return 1; } } diff --git a/src/solver.cu b/src/solver.cu index e6f8e21..1f08906 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -369,13 +369,20 @@ initialize_solver_state(const lp_problem_t *original_problem, for (int i = 0; i < n_vars; ++i) { - sum_of_squares += original_problem->objective_vector[i] * - original_problem->objective_vector[i]; val = fabs(original_problem->objective_vector[i]); - if (val > max_val) max_val = val; + if (state->optimality_norm == NORM_TYPE_L_INF) { + if (val > max_val) max_val = val; + } else { + sum_of_squares += original_problem->objective_vector[i] * + original_problem->objective_vector[i]; + } + } + + if (state->optimality_norm == NORM_TYPE_L_INF) { + state->objective_vector_norm = max_val; + } else { + state->objective_vector_norm = sqrt(sum_of_squares); } - state->objective_vector_norm = sqrt(sum_of_squares); - state->objective_vector_norm_inf = max_val; sum_of_squares = 0.0; max_val = 0.0; @@ -386,23 +393,30 @@ initialize_solver_state(const lp_problem_t *original_problem, double lower = original_problem->constraint_lower_bound[i]; double upper = original_problem->constraint_upper_bound[i]; - if (isfinite(lower) && (lower != upper)) - { - sum_of_squares += lower * lower; - val = fabs(lower); - if (val > max_val) max_val = val; - } - - if (isfinite(upper)) - { - sum_of_squares += upper * upper; - val = fabs(upper); - if (val > max_val) max_val = val; + if (state->optimality_norm == NORM_TYPE_L_INF) { + if (isfinite(lower) && (lower != upper)) { + val = fabs(lower); + if (val > max_val) max_val = val; + } + if (isfinite(upper)) { + val = fabs(upper); + if (val > max_val) max_val = val; + } + } else { + if (isfinite(lower) && (lower != upper)) { + sum_of_squares += lower * lower; + } + if (isfinite(upper)) { + sum_of_squares += upper * upper; + } } } - state->constraint_bound_norm = sqrt(sum_of_squares); - state->constraint_bound_norm_inf = max_val; + if (state->optimality_norm == NORM_TYPE_L_INF) { + state->objective_vector_norm = max_val; + } else { + state->objective_vector_norm = sqrt(sum_of_squares); + } state->num_blocks_primal = (state->num_variables + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; diff --git a/src/utils.cu b/src/utils.cu index 62a59c6..e1efe0c 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -727,18 +727,11 @@ void compute_residual(pdhg_solver_state_t *state) state->objective_vector_rescaling) + state->objective_constant; - if (state->optimality_norm == NORM_TYPE_L_INF) { - state->relative_primal_residual = - state->absolute_primal_residual / (1.0 + state->constraint_bound_norm_inf); - - state->relative_dual_residual = - state->absolute_dual_residual / (1.0 + state->objective_vector_norm_inf); - } else { - state->relative_primal_residual = - state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); - state->relative_dual_residual = - state->absolute_dual_residual / (1.0 + state->objective_vector_norm); - } + state->relative_primal_residual = + state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); + + state->relative_dual_residual = + state->absolute_dual_residual / (1.0 + state->objective_vector_norm); state->objective_gap = fabs(state->primal_objective_value - state->dual_objective_value); @@ -1242,11 +1235,7 @@ void compute_primal_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_ state->absolute_primal_residual /= state->constraint_bound_rescaling; - if (state->optimality_norm == NORM_TYPE_L_INF) { - state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm_inf); - } else { - state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); - } + state->relative_primal_residual = state->absolute_primal_residual / (1.0 + state->constraint_bound_norm); CUBLAS_CHECK(cublasDdot(state->blas_handle, state->num_variables, ori_state->objective_vector, 1, state->pdhg_primal_solution, 1, &state->primal_objective_value)); state->primal_objective_value = state->primal_objective_value / (state->constraint_bound_rescaling * state->objective_vector_rescaling) + state->objective_constant; @@ -1281,11 +1270,7 @@ void compute_dual_feas_polish_residual(pdhg_solver_state_t *state, const pdhg_so state->absolute_dual_residual /= state->objective_vector_rescaling; - if (state->optimality_norm == NORM_TYPE_L_INF) { - state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm_inf); - } else { - state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm); - } + state->relative_dual_residual = state->absolute_dual_residual / (1.0 + state->objective_vector_norm); double base_dual_objective; CUBLAS_CHECK(cublasDdot(state->blas_handle, state->num_variables, state->dual_slack, 1, ori_state->pdhg_primal_solution, 1, &base_dual_objective)); From 0c35c34cf6e8c31cc83099658fffdb4d6907516f Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Thu, 25 Dec 2025 13:19:13 -0500 Subject: [PATCH 15/17] Resolved conflicts --- include/cupdlpx_types.h | 1 + src/cli.c | 5 +++++ src/solver.cu | 5 ++--- src/utils.cu | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/cupdlpx_types.h b/include/cupdlpx_types.h index 2d86deb..d564fc9 100644 --- a/include/cupdlpx_types.h +++ b/include/cupdlpx_types.h @@ -98,6 +98,7 @@ extern "C" double reflection_coefficient; bool feasibility_polishing; norm_type_t optimality_norm; + bool presolve; } pdhg_parameters_t; typedef struct diff --git a/src/cli.c b/src/cli.c index 04c201b..c72e6e9 100644 --- a/src/cli.c +++ b/src/cli.c @@ -173,6 +173,8 @@ void print_usage(const char *prog_name) "polish tolerance (default: 1e-6).\n"); fprintf(stderr, " --opt_norm " "Norm for optimality criteria: l2 or linf (default: l2).\n"); + fprintf(stderr, " --no_presolve " + "Disable presolve (default: enabled).\n"); } int main(int argc, char *argv[]) @@ -198,6 +200,7 @@ int main(int argc, char *argv[]) {"sv_tol", required_argument, 0, 1012}, {"eval_freq", required_argument, 0, 1013}, {"opt_norm", required_argument, 0, 1014}, + {"no_presolve", no_argument, 0, 1015}, {0, 0, 0, 0}}; int opt; @@ -266,6 +269,8 @@ int main(int argc, char *argv[]) } } break; + case 1015: // --no_presolve + params.presolve = false; case '?': // Unknown option return 1; } diff --git a/src/solver.cu b/src/solver.cu index 1f08906..ab875fa 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -369,12 +369,11 @@ initialize_solver_state(const lp_problem_t *original_problem, for (int i = 0; i < n_vars; ++i) { - val = fabs(original_problem->objective_vector[i]); + val = fabs(working_problem->objective_vector[i]); if (state->optimality_norm == NORM_TYPE_L_INF) { if (val > max_val) max_val = val; } else { - sum_of_squares += original_problem->objective_vector[i] * - original_problem->objective_vector[i]; + sum_of_squares += working_problem->objective_vector[i] * working_problem->objective_vector[i]; } } diff --git a/src/utils.cu b/src/utils.cu index e1efe0c..40a0458 100644 --- a/src/utils.cu +++ b/src/utils.cu @@ -348,6 +348,7 @@ void set_default_parameters(pdhg_parameters_t *params) params->restart_params.i_smooth = 0.3; params->optimality_norm = NORM_TYPE_L2; + params->presolve = true; } #define PRINT_DIFF_INT(name, current, default_val) \ From 2cb0a41e6048c8b36e7e9f6fa306a764f5a5463b Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Thu, 25 Dec 2025 14:01:30 -0500 Subject: [PATCH 16/17] fixed minor bugs --- src/cli.c | 1 + src/solver.cu | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cli.c b/src/cli.c index dd7d728..f0bfe4e 100644 --- a/src/cli.c +++ b/src/cli.c @@ -301,6 +301,7 @@ int main(int argc, char *argv[]) break; case 1015: // --no_presolve params.presolve = false; + break; case '?': // Unknown option return 1; } diff --git a/src/solver.cu b/src/solver.cu index 3e97e2c..78f5792 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -415,8 +415,8 @@ initialize_solver_state(const pdhg_parameters_t *params, for (int i = 0; i < n_vars; ++i) { - val = fabs(working_problem->objective_vector[i]); if (state->optimality_norm == NORM_TYPE_L_INF) { + val = fabs(working_problem->objective_vector[i]); if (val > max_val) max_val = val; } else { sum_of_squares += working_problem->objective_vector[i] * working_problem->objective_vector[i]; @@ -458,9 +458,9 @@ initialize_solver_state(const pdhg_parameters_t *params, } if (state->optimality_norm == NORM_TYPE_L_INF) { - state->objective_vector_norm = max_val; + state->constraint_bound_norm = max_val; } else { - state->objective_vector_norm = sqrt(sum_of_squares); + state->constraint_bound_norm = sqrt(sum_of_squares); } state->num_blocks_primal = From b09d82b8a5b9bcd0211fe280bb7c738184bc139b Mon Sep 17 00:00:00 2001 From: Liubomir Maxim Baicev Date: Thu, 25 Dec 2025 18:40:59 -0500 Subject: [PATCH 17/17] Fixed minor bug, moved the declaration of optimality_norm to initialize_solver_state --- src/solver.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver.cu b/src/solver.cu index 78f5792..f1abacd 100644 --- a/src/solver.cu +++ b/src/solver.cu @@ -116,8 +116,6 @@ cupdlpx_result_t *optimize(const pdhg_parameters_t *params, rescale_info_t *rescale_info = rescale_problem(params, working_problem); pdhg_solver_state_t *state = initialize_solver_state(params, working_problem, rescale_info); - state->optimality_norm = params ? params->optimality_norm : NORM_TYPE_L2; - rescale_info_free(rescale_info); initialize_step_size_and_primal_weight(state, params); clock_t start_time = clock(); @@ -226,6 +224,8 @@ initialize_solver_state(const pdhg_parameters_t *params, pdhg_solver_state_t *state = (pdhg_solver_state_t *)safe_calloc(1, sizeof(pdhg_solver_state_t)); + state->optimality_norm = params ? params->optimality_norm : NORM_TYPE_L2; + int n_vars = working_problem->num_variables; int n_cons = working_problem->num_constraints; size_t var_bytes = n_vars * sizeof(double);