From 6d796420e232161408caf1d5434579835c3c07b2 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 8 Dec 2025 16:09:48 -0500 Subject: [PATCH 1/4] Docs: Refactor README --- README.md | 165 ++++++++++++-------------------------------------- docs/C_API.md | 95 +++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 127 deletions(-) create mode 100644 docs/C_API.md diff --git a/README.md b/README.md index afe8864..3094965 100644 --- a/README.md +++ b/README.md @@ -8,21 +8,30 @@ **cuPDLPx** is a GPU-accelerated linear programming solver based on a restarted Halpern PDHG method specifically tailored for GPU architectures. It incorporates a Halpern update scheme, an adaptive restart scheme, and a PID-controlled primal weight, resulting in substantial empirical improvements over its predecessor, **[cuPDLP](https://github.com/jinwen-yang/cuPDLP.jl)**, on standard LP benchmark suites. +cuPDLPx solves linear programs of the form +```math +\begin{aligned} +\min_{x} \quad & c^\top x \\ +\text{s.t.} \quad & \ell_c \le Ax \le u_c, \\ + & \ell_v \le x \le u_v. +\end{aligned} +``` + Our work is presented in two papers: * **Computational Paper:** [cuPDLPx: A Further Enhanced GPU-Based First-Order Solver for Linear Programming](https://arxiv.org/abs/2507.14051) details the practical innovations that give **cuPDLPx** its performance edge. * **Theoretical Paper:** [Restarted Halpern PDHG for Linear Programming](https://arxiv.org/pdf/2407.16144) provides the mathematical foundation for our method. -## Getting started -Follow these steps to build the solver and verify its installation. +## Installation ### Requirements -- An NVIDIA GPU with CUDA support (≥12.4 required). -- A C toolchain (gcc) and the NVIDIA CUDA Compiler (nvcc). +* **GPU:** NVIDIA GPU with CUDA 12.4+. +* **Build Tools:** CMake (≥ 3.22), GCC, NVCC. + ### Build from Source -Clone the repository and compile the project using `CMake`. +Clone the repository and compile the project using CMake. ```bash git clone git@github.com:MIT-Lu-Lab/cuPDLPx.git cd cuPDLPx @@ -31,7 +40,7 @@ cmake --build build --clean-first ``` This will create the solver binary at `./build/cupdlpx`. -### Verifying the Installation +#### Verifying the Installation Run a small test problem to confirm that the solver was built correctly. ```bash # 1. Download a test instance from the MIPLIB library @@ -42,11 +51,23 @@ wget -P test/ https://miplib.zib.de/WebData/instances/2club200v15p5scn.mps.gz ``` If the solver runs and creates output files, your installation is successful. -## Usage Guide +### Install Python package +You can install the Python package `cupdlpx` directly from PyPI: +```bash +pip install cupdlpx +``` +Or build from source: -### Command-Line Interface +``` +git clone https://github.com/MIT-Lu-Lab/cuPDLPx.git +cd cuPDLPx +pip install . +``` + +## Usage & Interfaces +### Command-line Interface -The solver is invoked with the following syntax, specifying an input file and an output directory. +After building the project, the `./build/cupdlpx` binary can be invoked from the command line as follows: ```bash ./build/cupdlpx [OPTIONS] @@ -72,129 +93,19 @@ The solver is invoked with the following syntax, specifying an input file and an ### Output Files The solver generates three text files in the specified . The filenames are derived from the input file's basename. For an input `INSTANCE.mps.gz`, the output will be: -- `INSTANCE_summary.txt`: Contains solver statistics, timings, and termination information. -- `INSTANCE_primal_solution.txt`: The primal solution vector. -- `INSTANCE_dual_solution.txt`: The dual solution vector. - -### Python Interface - -In addition to the command-line, cuPDLPx provides a Python interface (`cupdlpx`) -for building and solving LPs directly with NumPy and SciPy. - -- High-level, Pythonic API similar to commercial solvers. -- Supports dense and sparse matrices. -- Provides easy parameter management and solution attributes. - -Install from PyPI: - -```bash -pip install cupdlpx ``` - -Or build from source: - -```bash -git clone https://github.com/MIT-Lu-Lab/cuPDLPx.git -cd cuPDLPx -pip install . +/ +├── INSTANCE_summary.txt # Statistics, timings, and termination status +├── INSTANCE_primal_solution.txt # Primal solution vector +└── INSTANCE_dual_solution.txt # Dual solution vector ``` -See the [cupdlpx guide](https://github.com/MIT-Lu-Lab/cuPDLPx/tree/main/python/README.md) for full usage instructions, examples, and API details. +### Python Interface +The `cupdlpx` Python package supports building and solving LPs directly with `NumPy` and `SciPy`. +Documentation and examples are available in the [Python API Guide](python/README.md) ### C Interface - -Besides the command-line and Python interfaces, cuPDLPx also provides a C API for directly solving LPs in memory. This is useful when integrating cuPDLPx into other C/C++ projects or when generating problems programmatically. - -#### Functions and Parameters - -The C API involves two main functions: - -```c -lp_problem_t* create_lp_problem( - const double* c, // objective vector (length n) - const matrix_desc_t* A, // constraint matrix (m×n) - const double* con_lb, // constraint lower bounds (length m) - 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* c0 // scalar objective offset -); - -cupdlpx_result_t* solve_lp_problem( - const lp_problem_t* prob, - const pdhg_parameters_t* params // NULL → use default parameters -); -``` - -`create_lp_problem` parameters: -- `objective_c`: Objective vector. If NULL, defaults to all zeros. -- `A_desc`: Matrix descriptor. Supports `matrix_dense`, `matrix_csr`, `matrix_csc`, `matrix_coo`. -- `con_lb`: Constraint lower bounds. If NULL, defaults to all -INFINITY. -- `con_ub`: Constraint upper bounds. If NULL, defaults to all +INFINITY. -- `var_lb`: Variable lower bounds. If NULL, defaults to all 0.0. -- `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. - - -`solve_lp_problem` parameters: -- `prob`: An LP problem built with `create_LP_problem`. -- `params`: Solver parameters. If `NULL`, the solver will use default parameters. - -#### Feasibility Polishing (optional post-solve refinement) - -After the normal PDHG solve you can ask cuPDLPx to run an extra feasibility-polishing phase that tries to improve primal/dual feasibility. - -#### Example: Solving a Small LP -```c -#include "cupdlpx.h" -#include -#include - -int main() { - int m = 3; // number of constraints - int n = 2; // number of variables - - // Dense matrix A - double A[3][2] = { - {1.0, 2.0}, - {0.0, 1.0}, - {3.0, 2.0} - }; - - // Describe A - matrix_desc_t A_desc; - A_desc.m = m; A_desc.n = n; - A_desc.fmt = matrix_dense; - A_desc.zero_tolerance = 0.0; - A_desc.data.dense.A = &A[0][0]; - - // Objective coefficients - double c[2] = {1.0, 1.0}; - - // Constraint bounds: l <= A x <= u - double l[3] = {5.0, -INFINITY, -INFINITY}; - double u[3] = {5.0, 2.0, 8.0}; - - // Build the problem - lp_problem_t* prob = create_lp_problem( - c, &A_desc, l, u, NULL, NULL, NULL); - - // Solve (NULL → use default parameters) - cupdlpx_result_t* res = solve_lp_problem(prob, NULL); - - printf("Termination reason: %d\n", res->termination_reason); - printf("Primal objective: %.6f\n", res->primal_objective_value); - printf("Dual objective: %.6f\n", res->dual_objective_value); - for (int j = 0; j < res->num_variables; ++j) { - printf("x[%d] = %.6f\n", j, res->primal_solution[j]); - } - - lp_problem_free(prob); - cupdlpx_result_free(res); - - return 0; -} -``` +The public C API is defined in header file [`include/cupdlpx.h`](include/cupdlpx.h). A detailed description with usage examples can be found in the [C API Guide](docs/C_API.md). ## Reference If you use cuPDLPx or the ideas in your work, please cite the source below. diff --git a/docs/C_API.md b/docs/C_API.md new file mode 100644 index 0000000..27ab775 --- /dev/null +++ b/docs/C_API.md @@ -0,0 +1,95 @@ + +### C Interface + +cuPDLPx provides a C API for directly solving LPs in memory, defined in header file [`include/cupdlpx.h`](../include/cupdlpx.h). This is useful when integrating cuPDLPx into other C/C++ projects or when generating problems programmatically. + +#### Functions and Parameters + +The C API involves two main functions: + +```c +lp_problem_t *create_lp_problem( + const double *objective_c, // objective vector (length n) + const matrix_desc_t *A_desc, // constraint matrix (m×n) + const double *con_lb, // constraint lower bounds (length m) + 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 +); + +cupdlpx_result_t* solve_lp_problem( + const lp_problem_t* prob, + const pdhg_parameters_t* params // NULL → use default parameters +); +``` + +`create_lp_problem` parameters: +- `objective_c`: Objective vector. If NULL, defaults to all zeros. +- `A_desc`: Matrix descriptor. Supports `matrix_dense`, `matrix_csr`, `matrix_csc`, `matrix_coo`. +- `con_lb`: Constraint lower bounds. If NULL, defaults to all -INFINITY. +- `con_ub`: Constraint upper bounds. If NULL, defaults to all +INFINITY. +- `var_lb`: Variable lower bounds. If NULL, defaults to all 0.0. +- `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. + + +`solve_lp_problem` parameters: +- `prob`: An LP problem built with `create_LP_problem`. +- `params`: Solver parameters. If `NULL`, the solver will use default parameters. + +#### Feasibility Polishing (optional post-solve refinement) + +After the normal PDHG solve you can ask cuPDLPx to run an extra feasibility-polishing phase that tries to improve primal/dual feasibility. + +#### Example: Solving a Small LP +```c +#include "cupdlpx.h" +#include +#include + +int main() { + int m = 3; // number of constraints + int n = 2; // number of variables + + // Dense matrix A + double A[3][2] = { + {1.0, 2.0}, + {0.0, 1.0}, + {3.0, 2.0} + }; + + // Describe A + matrix_desc_t A_desc; + A_desc.m = m; A_desc.n = n; + A_desc.fmt = matrix_dense; + A_desc.zero_tolerance = 0.0; + A_desc.data.dense.A = &A[0][0]; + + // Objective coefficients + double c[2] = {1.0, 1.0}; + + // Constraint bounds: l <= A x <= u + double l[3] = {5.0, -INFINITY, -INFINITY}; + double u[3] = {5.0, 2.0, 8.0}; + + // Build the problem + lp_problem_t* prob = create_lp_problem( + c, &A_desc, l, u, NULL, NULL, NULL); + + // Solve (NULL → use default parameters) + cupdlpx_result_t* res = solve_lp_problem(prob, NULL); + + printf("Termination reason: %d\n", res->termination_reason); + printf("Primal objective: %.6f\n", res->primal_objective_value); + printf("Dual objective: %.6f\n", res->dual_objective_value); + for (int j = 0; j < res->num_variables; ++j) { + printf("x[%d] = %.6f\n", j, res->primal_solution[j]); + } + + lp_problem_free(prob); + cupdlpx_result_free(res); + + return 0; +} +``` \ No newline at end of file From 961c8304b7b3a315b751d37746db1fd08f7e1b65 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 8 Dec 2025 16:26:40 -0500 Subject: [PATCH 2/4] docs: update --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3094965..261e2f3 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,12 @@ wget -P test/ https://miplib.zib.de/WebData/instances/2club200v15p5scn.mps.gz ``` If the solver runs and creates output files, your installation is successful. -### Install Python package -You can install the Python package `cupdlpx` directly from PyPI: +### Python Package Installation +To use cuPDLPx in Python, you can install the pre-built package `cupdlpx` directly from PyPI: ```bash pip install cupdlpx ``` Or build from source: - ``` git clone https://github.com/MIT-Lu-Lab/cuPDLPx.git cd cuPDLPx @@ -73,11 +72,11 @@ After building the project, the `./build/cupdlpx` binary can be invoked from the ./build/cupdlpx [OPTIONS] ``` -### Arguments +#### Arguments - ``: The path to the input linear programming problem. Both plain (`.mps`) and gzipped (`.mps.gz`) files are supported. - ``: The directory where the output files will be saved. -### Solver Options +#### Solver Options | Option | Type | Description | Default | | :--- | :--- | :--- | :--- | @@ -91,7 +90,7 @@ After building the project, the `./build/cupdlpx` binary can be invoked from the | `-f`,`--feasibility_polishing` |`flag` | Run the polishing loop | `false` | | `--eps_feas_polish` | `double` | Relative tolerance for polishing | `1e-6` | -### Output Files +#### Output Files The solver generates three text files in the specified . The filenames are derived from the input file's basename. For an input `INSTANCE.mps.gz`, the output will be: ``` / From 41404d8c91b79f3d687bd012795a7b6c2abcf7a5 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 8 Dec 2025 16:27:42 -0500 Subject: [PATCH 3/4] docs: update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 261e2f3..ea14b47 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ The solver generates three text files in the specified . The f ### Python Interface The `cupdlpx` Python package supports building and solving LPs directly with `NumPy` and `SciPy`. -Documentation and examples are available in the [Python API Guide](python/README.md) +Documentation and examples are available in the [Python API Guide](python/README.md). ### C Interface The public C API is defined in header file [`include/cupdlpx.h`](include/cupdlpx.h). A detailed description with usage examples can be found in the [C API Guide](docs/C_API.md). From c9f94fbb36e4be123022a83791f09eb07d47abf8 Mon Sep 17 00:00:00 2001 From: ZedongPeng Date: Mon, 8 Dec 2025 16:43:07 -0500 Subject: [PATCH 4/4] change default var_lb to -inf --- docs/C_API.md | 16 ++++++---------- src/cupdlpx.c | 2 +- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/docs/C_API.md b/docs/C_API.md index 27ab775..24bc5c9 100644 --- a/docs/C_API.md +++ b/docs/C_API.md @@ -25,23 +25,19 @@ cupdlpx_result_t* solve_lp_problem( ``` `create_lp_problem` parameters: -- `objective_c`: Objective vector. If NULL, defaults to all zeros. +- `objective_c`: Objective vector. If `NULL`, defaults to all zeros. - `A_desc`: Matrix descriptor. Supports `matrix_dense`, `matrix_csr`, `matrix_csc`, `matrix_coo`. -- `con_lb`: Constraint lower bounds. If NULL, defaults to all -INFINITY. -- `con_ub`: Constraint upper bounds. If NULL, defaults to all +INFINITY. -- `var_lb`: Variable lower bounds. If NULL, defaults to all 0.0. -- `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. +- `con_lb`: Constraint lower bounds. If `NULL`, defaults to all `-INFINITY`. +- `con_ub`: Constraint upper bounds. If `NULL`, defaults to all `+INFINITY`. +- `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`. `solve_lp_problem` parameters: - `prob`: An LP problem built with `create_LP_problem`. - `params`: Solver parameters. If `NULL`, the solver will use default parameters. -#### Feasibility Polishing (optional post-solve refinement) - -After the normal PDHG solve you can ask cuPDLPx to run an extra feasibility-polishing phase that tries to improve primal/dual feasibility. - #### Example: Solving a Small LP ```c #include "cupdlpx.h" diff --git a/src/cupdlpx.c b/src/cupdlpx.c index eea138e..1b0fb80 100644 --- a/src/cupdlpx.c +++ b/src/cupdlpx.c @@ -110,7 +110,7 @@ lp_problem_t *create_lp_problem(const double *objective_c, // default fill values prob->objective_constant = objective_constant ? *objective_constant : 0.0; 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, 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); fill_or_copy(&prob->constraint_lower_bound, prob->num_constraints, con_lb,