diff --git a/.github/workflows/main-arm64.yml b/.github/workflows/main-arm64.yml index c6ac4d8..0cc310a 100644 --- a/.github/workflows/main-arm64.yml +++ b/.github/workflows/main-arm64.yml @@ -28,9 +28,9 @@ jobs: run: | mkdir -p venvs uv venv venvs/cu12 - uv pip install --python venvs/cu12 --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==26.8.*' -qq + uv pip install --python venvs/cu12 --prerelease=allow --index-strategy unsafe-best-match --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple --extra-index-url=https://pypi.nvidia.com 'libcuopt-cu12==26.10.0a223.post261002122804' -qq uv venv venvs/cu13 - uv pip install --python venvs/cu13 --extra-index-url=https://pypi.nvidia.com 'cuopt-cu13==26.8.*' -qq + uv pip install --python venvs/cu13 --prerelease=allow --index-strategy unsafe-best-match --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple --extra-index-url=https://pypi.nvidia.com 'libcuopt-cu13==26.10.0a223.post261002122804' -qq # Get GAMS (ARM64 version) - name: Download and extract latest GAMS distribution @@ -51,7 +51,7 @@ jobs: gcc -Wall gmscuopt.c -o gmscuopt-cu12.out \ -DCUOPT_VERSION=\"$CUOPT_VERSION\" -DCUOPT_HASH=\"$CUOPT_HASH\" \ -I $GAMSCAPI $GAMSCAPI/gmomcc.c $GAMSCAPI/optcc.c $GAMSCAPI/gevmcc.c \ - -I $CUOPT/include $JITLINK/libnvJitLink.so.12 -L $CUOPT/lib64 -lcuopt + -I $CUOPT/include $JITLINK/libnvJitLink.so.12 -L $CUOPT/lib64 -lcuopt_mathopt patchelf --set-rpath \$ORIGIN gmscuopt-cu12.out export CUOPT="venvs/cu13/lib/python3.13/site-packages/libcuopt" export JITLINK="venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib" @@ -60,7 +60,7 @@ jobs: gcc -Wall gmscuopt.c -o gmscuopt-cu13.out \ -DCUOPT_VERSION=\"$CUOPT_VERSION\" -DCUOPT_HASH=\"$CUOPT_HASH\" \ -I $GAMSCAPI $GAMSCAPI/gmomcc.c $GAMSCAPI/optcc.c $GAMSCAPI/gevmcc.c \ - -I $CUOPT/include $JITLINK/libnvJitLink.so.13 -L $CUOPT/lib64 -lcuopt + -I $CUOPT/include $JITLINK/libnvJitLink.so.13 -L $CUOPT/lib64 -lcuopt_mathopt patchelf --set-rpath \$ORIGIN gmscuopt-cu13.out # Collect dependencies for link and runtime convenience archive @@ -69,17 +69,18 @@ jobs: mkdir release-cu12 cp gmscuopt-cu12.out release-cu12/gmscuopt.out cp assets/* release-cu12/ - cp venvs/cu12/lib/python3.13/site-packages/libcuopt/lib64/libcuopt.so release-cu12/ - cp venvs/cu12/lib/python3.13/site-packages/libraft_cu12.libs/libgomp-*.so* release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt/lib64/libcuopt_mathopt.so release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt/lib64/libcuopt_client.so release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libgomp-*.so* release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libtbb-*.so* release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libtbbmalloc-*.so* release-cu12/ - cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libomp-*.so* release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt/lib64/libcudss_mtlayer_cuopt.so release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libcudart-*.so* release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/rapids_logger/lib64/librapids_logger.so release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/librmm/lib64/librmm.so release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/nvidia/nccl/lib/libnccl.so* release-cu12/ mkdir runtime-cu12 cp venvs/cu12/lib/python3.13/site-packages/nvidia/cu12/lib/libcudss.so* runtime-cu12/ - cp venvs/cu12/lib/python3.13/site-packages/nvidia/cu12/lib/libcudss_mtlayer_gomp.so* runtime-cu12/ cp venvs/cu12/lib/python3.13/site-packages/nvidia/cusolver/lib/libcusolver.so* runtime-cu12/ cp venvs/cu12/lib/python3.13/site-packages/nvidia/cublas/lib/libcublas.so* runtime-cu12/ cp venvs/cu12/lib/python3.13/site-packages/nvidia/cublas/lib/libcublasLt.so* runtime-cu12/ @@ -90,17 +91,18 @@ jobs: mkdir release-cu13 cp gmscuopt-cu13.out release-cu13/gmscuopt.out cp assets/* release-cu13/ - cp venvs/cu13/lib/python3.13/site-packages/libcuopt/lib64/libcuopt.so release-cu13/ - cp venvs/cu13/lib/python3.13/site-packages/libraft_cu13.libs/libgomp-*.so* release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt/lib64/libcuopt_mathopt.so release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt/lib64/libcuopt_client.so release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libgomp-*.so* release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libtbb-*.so* release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libtbbmalloc-*.so* release-cu13/ - cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libomp-*.so* release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt/lib64/libcudss_mtlayer_cuopt.so release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libcudart-*.so* release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/rapids_logger/lib64/librapids_logger.so release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/librmm/lib64/librmm.so release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/nvidia/nccl/lib/libnccl.so* release-cu13/ mkdir runtime-cu13 cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libcudss.so* runtime-cu13/ - cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libcudss_mtlayer_gomp.so* runtime-cu13/ cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libnvJitLink.so* runtime-cu13/ cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libcublas.so* runtime-cu13/ cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libcublasLt.so* runtime-cu13/ diff --git a/.github/workflows/main-x86_64.yml b/.github/workflows/main-x86_64.yml index 535d4bd..4b1f3dc 100644 --- a/.github/workflows/main-x86_64.yml +++ b/.github/workflows/main-x86_64.yml @@ -28,9 +28,9 @@ jobs: run: | mkdir -p venvs uv venv venvs/cu12 - uv pip install --python venvs/cu12 --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==26.8.*' -qq + uv pip install --python venvs/cu12 --prerelease=allow --index-strategy unsafe-best-match --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple --extra-index-url=https://pypi.nvidia.com 'libcuopt-cu12==26.10.0a223.post261002122804' -qq uv venv venvs/cu13 - uv pip install --python venvs/cu13 --extra-index-url=https://pypi.nvidia.com 'cuopt-cu13==26.8.*' -qq + uv pip install --python venvs/cu13 --prerelease=allow --index-strategy unsafe-best-match --extra-index-url=https://pypi.anaconda.org/rapidsai-wheels-nightly/simple --extra-index-url=https://pypi.nvidia.com 'libcuopt-cu13==26.10.0a223.post261002122804' -qq # Get GAMS - name: Download and extract latest GAMS distribution @@ -51,7 +51,7 @@ jobs: gcc -Wall gmscuopt.c -o gmscuopt-cu12.out \ -DCUOPT_VERSION=\"$CUOPT_VERSION\" -DCUOPT_HASH=\"$CUOPT_HASH\" \ -I $GAMSCAPI $GAMSCAPI/gmomcc.c $GAMSCAPI/optcc.c $GAMSCAPI/gevmcc.c \ - -I $CUOPT/include $JITLINK/libnvJitLink.so.12 -L $CUOPT/lib64 -lcuopt + -I $CUOPT/include $JITLINK/libnvJitLink.so.12 -L $CUOPT/lib64 -lcuopt_mathopt patchelf --set-rpath \$ORIGIN gmscuopt-cu12.out export CUOPT="venvs/cu13/lib/python3.13/site-packages/libcuopt" export JITLINK="venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib" @@ -60,7 +60,7 @@ jobs: gcc -Wall gmscuopt.c -o gmscuopt-cu13.out \ -DCUOPT_VERSION=\"$CUOPT_VERSION\" -DCUOPT_HASH=\"$CUOPT_HASH\" \ -I $GAMSCAPI $GAMSCAPI/gmomcc.c $GAMSCAPI/optcc.c $GAMSCAPI/gevmcc.c \ - -I $CUOPT/include $JITLINK/libnvJitLink.so.13 -L $CUOPT/lib64 -lcuopt + -I $CUOPT/include $JITLINK/libnvJitLink.so.13 -L $CUOPT/lib64 -lcuopt_mathopt patchelf --set-rpath \$ORIGIN gmscuopt-cu13.out # Collect dependencies for link and runtime convenience archive @@ -69,17 +69,18 @@ jobs: mkdir release-cu12 cp gmscuopt-cu12.out release-cu12/gmscuopt.out cp assets/* release-cu12/ - cp venvs/cu12/lib/python3.13/site-packages/libcuopt/lib64/libcuopt.so release-cu12/ - cp venvs/cu12/lib/python3.13/site-packages/libraft_cu12.libs/libgomp-*.so* release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt/lib64/libcuopt_mathopt.so release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt/lib64/libcuopt_client.so release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libgomp-*.so* release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libtbb-*.so* release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libtbbmalloc-*.so* release-cu12/ - cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libomp-*.so* release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt/lib64/libcudss_mtlayer_cuopt.so release-cu12/ + cp venvs/cu12/lib/python3.13/site-packages/libcuopt_cu12.libs/libcudart-*.so* release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/rapids_logger/lib64/librapids_logger.so release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/librmm/lib64/librmm.so release-cu12/ cp venvs/cu12/lib/python3.13/site-packages/nvidia/nccl/lib/libnccl.so* release-cu12/ mkdir runtime-cu12 cp venvs/cu12/lib/python3.13/site-packages/nvidia/cu12/lib/libcudss.so* runtime-cu12/ - cp venvs/cu12/lib/python3.13/site-packages/nvidia/cu12/lib/libcudss_mtlayer_gomp.so* runtime-cu12/ cp venvs/cu12/lib/python3.13/site-packages/nvidia/cusolver/lib/libcusolver.so* runtime-cu12/ cp venvs/cu12/lib/python3.13/site-packages/nvidia/cublas/lib/libcublas.so* runtime-cu12/ cp venvs/cu12/lib/python3.13/site-packages/nvidia/cublas/lib/libcublasLt.so* runtime-cu12/ @@ -90,17 +91,18 @@ jobs: mkdir release-cu13 cp gmscuopt-cu13.out release-cu13/gmscuopt.out cp assets/* release-cu13/ - cp venvs/cu13/lib/python3.13/site-packages/libcuopt/lib64/libcuopt.so release-cu13/ - cp venvs/cu13/lib/python3.13/site-packages/libraft_cu13.libs/libgomp-*.so* release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt/lib64/libcuopt_mathopt.so release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt/lib64/libcuopt_client.so release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libgomp-*.so* release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libtbb-*.so* release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libtbbmalloc-*.so* release-cu13/ - cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libomp-*.so* release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt/lib64/libcudss_mtlayer_cuopt.so release-cu13/ + cp venvs/cu13/lib/python3.13/site-packages/libcuopt_cu13.libs/libcudart-*.so* release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/rapids_logger/lib64/librapids_logger.so release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/librmm/lib64/librmm.so release-cu13/ cp venvs/cu13/lib/python3.13/site-packages/nvidia/nccl/lib/libnccl.so* release-cu13/ mkdir runtime-cu13 cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libcudss.so* runtime-cu13/ - cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libcudss_mtlayer_gomp.so* runtime-cu13/ cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libnvJitLink.so* runtime-cu13/ cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libcublas.so* runtime-cu13/ cp venvs/cu13/lib/python3.13/site-packages/nvidia/cu13/lib/libcublasLt.so* runtime-cu13/ diff --git a/README.md b/README.md index bc3cf0d..3122010 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,9 @@ Supported model types are LP, MIP, RMIP, QCP, RMIQCP. QCP and RMIQCP models must - **CPU architecture:** x86_64, arm64 - **GAMS:** Version 54 or newer - **GAMSPy:** Version 1.12.1 or newer -- **NVIDIA GPU:** Volta architecture or better +- **NVIDIA GPU:** Volta architecture or better with CUDA 12, Turing architecture or better with CUDA 13 - **CUDA Runtime Libraries:** 12 or 13 +- **System libraries:** OpenSSL 3 (`libssl.so.3`, `libcrypto.so.3`) and zlib (`libz.so.1`) ## Installation using `fetch-cuoptlink.py` @@ -70,9 +71,9 @@ python fetch-cuoptlink.py uninstall -g /opt/gams/gams55.0 - Make sure [CUDA runtime](https://developer.nvidia.com/cuda-downloads?target_os=Linux) is installed - Download and unpack `cuopt-link-release-cu12-{x86_64,arm64}.zip` or `cuopt-link-release-cu13-{x86_64,arm64}.zip` (for CUDA 12 and 13 respectively) from the [releases page](https://github.com/GAMS-dev/cuoptlink-builder/releases): - Unpack the contents of `cuopt-link-release-cu*-*.zip` into your GAMS system directory. For GAMSPy, you can find out your system directory by running `gamspy show base`. So for example you can run `unzip -o cuopt-link-release-cu*-*.zip -d $(gamspy show base)`. - - **Caution:** This will overwrite any existing `gamsconfig.yaml` file in that directory. The contained `gamsconfig.yaml` contains a `solverConfig` section to make cuOpt available to GAMS. + - The archive contains a `gamsconfig_cuopt.yaml` with a `solverConfig` section that makes cuOpt available to GAMS. If the GAMS system directory has no `gamsconfig.yaml` yet, copy it, e.g. `cp gamsconfig_cuopt.yaml gamsconfig.yaml`. Otherwise add its `solverConfig` entry to the existing `gamsconfig.yaml` (`fetch-cuoptlink.py` does this merge automatically). -The neccessary files from the CUDA 12 or 13 runtime can also be downloaded as convenient archive `cu12-runtime-{x86_64,arm64}.zip` or `cu13-runtime-{x86_64,arm64}.zip` from the [releases page](https://github.com/GAMS-dev/cuoptlink-builder/releases). +The necessary files from the CUDA 12 or 13 runtime can also be downloaded as convenient archive `cu12-runtime-{x86_64,arm64}.zip` or `cu13-runtime-{x86_64,arm64}.zip` from the [releases page](https://github.com/GAMS-dev/cuoptlink-builder/releases). ## Test the setup @@ -82,16 +83,36 @@ gamslib trnsport gams trnsport lp cuopt ``` +## Multi-GPU PDLP + +With cuOpt 26.10 or newer, LPs can be solved with PDLP on several GPUs at once. This requires both `method 1` (PDLP) and `num_gpus -1` (all visible GPUs) or `num_gpus` greater than 1 in the option file: +``` +* cuopt.opt +method 1 +num_gpus -1 +``` +``` +gams mymodel lp=cuopt optfile=1 +``` + +- Without `method 1`, i.e. in the default concurrent mode, `num_gpus 2` instead runs PDLP and barrier in parallel on two GPUs. +- `multigpu_pdlp_partitioner` selects how the problem is split across the GPUs: `0` auto (default), `1` KaMinPar (better balanced, extra partitioning time), `2` round robin. +- The GPUs used can be restricted with `CUDA_VISIBLE_DEVICES`, e.g. `CUDA_VISIBLE_DEVICES=0,1 gams mymodel lp=cuopt optfile=1`. +- Only LPs are supported, and the whole problem currently has to fit into the memory of a single GPU. +- Starting values (levels and marginals) from GAMS are not passed to multi-GPU PDLP. +- Convergence can differ from single-GPU PDLP, so some models need noticeably more iterations. + ## Examples ### Notebooks - [examples/trnsport_cuopt.ipynb](examples/trnsport_cuopt.ipynb) for CUDA 12 on x86_64 -- [examples/trnsport_cuopt.ipynb](examples/trnsport_cuopt_cu13.ipynb) for CUDA 13 on x86_64 +- [examples/trnsport_cuopt_cu13.ipynb](examples/trnsport_cuopt_cu13.ipynb) for CUDA 13 on x86_64 ### GAMS models Various GAMS models can be found in subfolder `examples/models` and are used to verify the solver link. + ### Regression tests The self-checking models in `examples/models/regression_tests` cover dual signs and reduced costs, QP/QCQP marginals, RMIQCP, option handling, error reporting, LP limit points, GMO handling (e.g. `=N=` rows, `requestMarginals=2`), rejection of unsupported features (SOS, semi-integer, MIQCP) and solve/model status mapping. Each model aborts if a result deviates from the reference values (obtained with CPLEX). Run them all against the GAMS system found in your `PATH` (it needs a GPU and the installed solver link): @@ -101,3 +122,13 @@ examples/models/regression_tests/run_tests.sh ``` The script prints `[PASS]` or `[FAIL]` per model and keeps the listing and log file of failed models for inspection. Its exit code is the number of failed models. + +### gamslib regression test + +`tests/test-gamslib-cuopt.py` solves the gamslib models listed in `tests/baseline.txt` with cuOpt and compares the objective values against a CPLEX baseline. It needs a GPU and the installed solver link: + +``` +python3 tests/test-gamslib-cuopt.py -g -j 2 # -g defaults to the GAMS found in PATH +``` + +Use `-r` to change the time limit per model (default 120s; an optional fourth column in `tests/baseline.txt` sets a model-specific time limit instead), `-t` for the relative tolerance (default 1e-6) and pass model names to run only a subset. The exit code is 1 if any model mismatches or fails to produce a solution. diff --git a/assets/optcuopt.def b/assets/optcuopt.def index 2184850..329f922 100644 --- a/assets/optcuopt.def +++ b/assets/optcuopt.def @@ -3,7 +3,11 @@ * * name type usermap default low high visible group num_cpu_threads integer 0 -1 -1 maxint 1 1 Controls the number of CPU threads used in the LP and MIP solvers (default GAMS Threads) -num_gpus integer 0 1 1 2 1 2 Controls the number of GPUs to use for the solve. This setting is only relevant for LP problems that uses concurrent mode and supports up to 2 GPUs at the moment. Using this mode will run PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources. +num_gpus integer 0 1 -1 72 1 2 Controls the number of GPUs to use for the solve. In concurrent mode for LP problems up to 2 GPUs are used, running PDLP and barrier in parallel on different GPUs to avoid sharing single GPU resources. With method pdlp, a value of -1 (all visible GPUs) or greater than 1 runs multi-GPU PDLP, which shards the LP across GPUs (the problem must currently fit into the memory of a single GPU). +multigpu_pdlp_partitioner enumint 0 0 1 2 Controls how multi-GPU PDLP partitions the problem across GPUs + 0 1 auto (default) RoundRobin on 1 GPU, KaMinPar otherwise + 1 1 KaMinPar multi-threaded graph partitioner, better balanced shards at the cost of extra partitioning time + 2 1 RoundRobin no partitioning graph built presolve enumint 0 -1 1 1 Controls which presolver (if any) to use for presolve reductions. -1 1 default (Papilo for MIP, PSLP for LP) 0 1 off (disable presolve) @@ -30,6 +34,7 @@ method enumint 0 0 1 2 Controls the method to solve the linear programming probl 1 1 pdlp Use the PDLP method. 2 1 dual_simplex Use the dual simplex method. 3 1 method_barrier Use the barrier (interior-point) method. + 4 1 primal_simplex Use the primal simplex method. pdlp_precision enumint 0 -1 1 2 PDLP precision mode constants -1 1 default By default, PDLP operates in the native precision of the problem type (FP64 for double-precision problems). 0 1 single Run PDLP internally in FP32, with automatic conversion of inputs and outputs. FP32 uses half the memory and allows PDHG iterations to be on average twice as fast, but may require more iterations to converge. Compatible with crossover (solution is converted back to FP64 before crossover) and concurrent mode (PDLP runs in FP32 while other solvers run in FP64). @@ -39,6 +44,13 @@ iteration_limit integer 0 maxint 0 maxint 1 2 Controls the iteration limit after infeasibility_detection boolean 0 0 1 2 Controls whether PDLP should detect infeasibility strict_infeasibility boolean 0 0 1 2 Controls the strict infeasibility mode in PDLP crossover boolean 0 0 1 2 Controls whether PDLP should crossover to a basic solution after an optimal solution is found +concurrent_nnz_cutoff integer 0 50000000 -1 maxint 1 2 Controls the reduced number of nonzeros at or above which barrier and dual simplex are skipped in concurrent mode (-1 disables the cutoff) +primal_simplex_pricing enumint 0 1 1 2 Controls the pricing rule of the primal simplex method + 0 1 Dantzig pricing + 1 1 Devex pricing (default) +dual_simplex_initial_perturbation integer 0 -1 -1 1 1 2 Controls whether the simplex method perturbs the problem initially (-1 automatic, 0 off, 1 on) +dual_simplex_remove_perturbation integer 0 -1 -1 1 1 2 Controls whether the simplex method removes the perturbation (-1 automatic, 0 off, 1 on) +pdlp_hyper_enable_curtis_reid_scaling boolean 0 1 1 2 Controls whether Curtis-Reid prescaling runs before Ruiz/Pock-Chambolle scaling in PDLP save_best_primal_so_far boolean 0 0 1 2 Controls whether PDLP should save the best primal solution so far first_primal_feasible boolean 0 0 1 2 Controls whether PDLP should stop when the first primal feasible solution is found per_constraint_residual boolean 0 0 1 2 Controls whether PDLP should compute the primal & dual residual per constraint instead of globally @@ -64,8 +76,15 @@ barrier_dual_initial_point enumint 0 -1 1 4 Controls the method used to compute -1 1 automatic (default) cuOpt selects the best method 0 1 use an initial point from a heuristic approach based on the paper "On Implementing Mehrotra’s Predictor–Corrector Interior-Point Method for Linear Programming" (SIAM J. Optimization, 1992) by Lustig, Martsten, Shanno. 1 1 use an initial point from solving a least squares problem that minimizes the norms of the dual variables and reduced costs while statisfying the dual equality constraints. + 2 1 use a Sturm/SeDuMi-style mu-based primal and dual initial point. barrier_iterative_refinement boolean 0 1 1 4 Controls whether iterative refinement is applied after each barrier iteration to improve solution accuracy. qcqp_hyper_ruiz_equilibration integer 0 -1 -1 1 1 4 Controls Ruiz equilibration for QCQP (barrier) scaling (-1 automatic, 0 off, 1 on) +barrier_adaptive_regularization integer 0 -1 -1 1 1 4 Controls adaptive regularization in the barrier method (-1 automatic, 0 off, 1 on) +barrier_primal_regularization double 0 -1 -1 maxdouble 1 4 Controls the initial primal regularization for the augmented system (-1 automatic) +barrier_dual_regularization double 0 -1 -1 maxdouble 1 4 Controls the initial dual regularization for the augmented system (-1 automatic) +barrier_initial_point_safeguard double 0 10 0 maxdouble 1 4 Controls the margin pushing the barrier initial iterate into the interior of the nonnegative orthant / second-order cone +barrier_presolve_bound_free_variables integer 0 -1 -1 1 1 4 Controls whether free variables are bounded during barrier presolve (-1 automatic, 0 off, 1 on) +barrier_hyper_gpu_ruiz_nnz_threshold integer 0 500000 0 maxint 1 4 Controls the number of nonzeros (of A and Q) at or above which barrier Ruiz equilibration runs on the GPU instead of the CPU barrier_step_scale double 0 0.9 0.5 0.9999 1 4 Controls the scaling factor applied to the step size in the barrier method. absolute_primal_tolerance double 0 0.0001 0 0.1 1 2 Controls the absolute primal tolerance used in PDLP's primal feasibility check relative_primal_tolerance double 0 0.0001 0 0.1 1 2 Controls the relative primal tolerance used in PDLP's primal feasibility check @@ -105,6 +124,8 @@ mip_implied_bound_cuts integer 0 -1 -1 1 1 3 Controls whether to enable implied mip_clique_cuts integer 0 -1 -1 1 1 3 Controls whether to enable clique cuts (-1 auto/default, 0 off, >0 on). mip_zero_half_cuts integer 0 -1 -1 1 1 3 Controls whether to use zero-half cuts (-1 automatic, 0 off, 1 on) mip_rins integer 0 -1 -1 1 1 3 Controls whether to use (recursive) RINS (-1 automatic, 0 off, 1 on) +mip_rens integer 0 -1 -1 1 1 3 Controls whether to use the RENS (relaxation enforced neighborhood search) heuristic (-1 automatic, 0 off, 1 on) +mip_mutation integer 0 -1 -1 1 1 3 Controls whether to use the mutation heuristic, which fixes a random subset of integer variables to their incumbent values (-1 automatic, 0 off, 1 on) mip_determinism_mode enumint 0 0 1 3 Controls whether the MIP solver runs in opportunistic or deterministic mode 0 1 opportunistic, results may vary between runs due to parallelism (default) 1 1 deterministic (experimental), improves reproducibility across runs with the same number of threads @@ -124,8 +145,6 @@ mip_reliability_branching integer 0 -1 -1 maxint 1 3 Controls the reliablity bra mip_strong_branching_simplex_iteration_limit integer 0 -1 -1 maxint 1 3 Controls the strong branching simplex iteration limit mip_hyper_heuristic_population_size integer 0 32 1 maxint 1 3 Controls maximum number of solutions in pool mip_hyper_heuristic_num_cpufj_threads integer 0 8 0 maxint 1 3 Controls parallel CPU FJ climbers -mip_hyper_heuristic_presolve_time_ratio double 0 0.1 0 1 1 3 Controls fraction of total time for presolve -mip_hyper_heuristic_presolve_max_time double 0 60 0 maxdouble 1 3 Controls the hard cap on presolve seconds mip_hyper_heuristic_root_lp_time_ratio double 0 0.1 0 1 1 3 Controls fraction of total time for root LP mip_hyper_heuristic_root_lp_max_time double 0 15 0 maxdouble 1 3 Controls the hard cap on root LP seconds mip_hyper_heuristic_rins_time_limit double 0 3 0 maxdouble 1 3 Controls per-call RINS sub-MIP time @@ -138,14 +157,13 @@ mip_hyper_heuristic_n_of_minimums_for_exit integer 0 7000 1 maxint 1 3 Controls mip_hyper_heuristic_enabled_recombiners integer 0 15 0 15 1 3 Controls bitmask: 1=BP 2=FP 4=LS 8=SubMIP mip_hyper_heuristic_cycle_detection_length integer 0 30 1 maxint 1 3 Controls FP assignment cycle ring buffer mip_hyper_heuristic_relaxed_lp_time_limit double 0 1 1e-09 maxdouble 1 3 Controls base relaxed LP time cap in heuristics -mip_hyper_heuristic_related_vars_time_limit double 0 30 1e-09 maxdouble 1 3 Controls time for related-variable structure build +mip_hyper_heuristic_related_vars_time_limit double 0 2 1e-09 maxdouble 1 3 Controls time for related-variable structure build mip_hyper_diving_line_search integer 0 -1 -1 1 1 3 Controls diving heuristic line search (-1 automatic, 0 disabled, 1 enabled) mip_hyper_diving_pseudocost integer 0 -1 -1 1 1 3 Controls diving heuristic pseudocost (-1 automatic, 0 disabled, 1 enabled) mip_hyper_diving_guided integer 0 -1 -1 1 1 3 Controls diving heuristic guided diving (-1 automatic, 0 disabled, 1 enabled) mip_hyper_diving_coefficient integer 0 -1 -1 1 1 3 Controls diving heuristic coefficient (-1 automatic, 0 disabled, 1 enabled) mip_hyper_diving_farkas integer 0 -1 -1 1 1 3 Controls diving heuristic farkas (-1 automatic, 0 disabled, 1 enabled) mip_hyper_diving_vector_length integer 0 -1 -1 1 1 3 Controls diving heuristic vector-length (-1 automatic, 0 disabled, 1 enabled) -mip_hyper_diving_min_node_depth integer 0 10 0 maxint 1 3 Controls minimum depth at which to start diving mip_hyper_diving_node_limit integer 0 500 0 maxint 1 3 Controls nodes explored per dive mip_hyper_diving_iteration_limit_factor double 0 0.05 0 1 1 3 Fraction of best-first iterations allowed per dive mip_hyper_diving_backtrack_limit integer 0 5 0 32767 1 3 Controls maximum backtracking allowed per dive @@ -153,10 +171,15 @@ mip_hyper_submip_base_target_fixrate double 0 0.6 0 1 1 3 Controls base target f mip_hyper_submip_min_fixrate double 0 0.25 0 1 1 3 Controls minimum fix rate for accepting the RINS neighbourhood mip_hyper_submip_min_fixrate_cap double 0 0.1 0 1 1 3 Controls hard cap on the minimum fix rate for solving a sub-MIP mip_hyper_submip_target_mip_gap double 0 0.01 0 1 1 3 Controls MIP gap target for the sub-MIP -mip_hyper_submip_node_limit_base integer 0 200 0 maxint 1 3 Controls base node limit for the sub-MIP mip_hyper_submip_max_level integer 0 10 0 maxint 1 3 Controls maximum sub-MIP recursion level mip_hyper_submip_iteration_limit_ratio double 0 0.8 0 1 1 3 Controls sub-MIP simplex iteration limit as a factor of the parent branch-and-bound iterations mip_hyper_submip_enable_cpufj boolean 0 1 1 3 Controls whether CPU feasibility jump runs over the sub-MIP +mip_hyper_submip_node_limit_offset integer 0 200 0 maxint 1 3 Controls base node limit for the sub-MIP +mip_hyper_submip_iteration_limit_offset integer 0 10000 0 maxint 1 3 Controls base sub-MIP simplex iteration limit for root heuristics +mip_hyper_submip_round_close_ratio double 0 0.8 0 1 1 3 Controls share of the still unfixed integers left for later neighbourhood rounds (0 reaches the target fix rate in a single round) +mip_hyper_block_bve boolean 0 1 1 3 Controls whether blocks of binaries are eliminated in cuOpt's MIP presolve (needs mip_probing) +mip_hyper_presolve_indicator_strengthening boolean 0 1 1 3 Controls whether implied indicator rows are appended and capacity rows are lifted by their indicator before Papilo presolve (MIP only) +mip_hyper_heuristic_presolve_max_rounds integer 0 -1 -1 maxint 1 3 Controls the Papilo presolve rounds cap in heuristics (<0 derives it from the problem, 0 keeps the Papilo default) mip_hyper_diving_show_type boolean 0 0 1 3 log diving heuristic type when it finds a new incumbent mip_semi_continuous_big_m double 0 1e10 1 maxdouble 1 3 Controls the Big-M coefficient used when linearizing semi-continuous variable constraints diff --git a/build-link.sh b/build-link.sh index 07f1ed3..bbdc987 100755 --- a/build-link.sh +++ b/build-link.sh @@ -40,7 +40,7 @@ gcc -g3 -O0 -Wall gmscuopt.c -o gmscuopt-cu13.out \ -DCUOPT_HASH=\"$CUOPT_HASH\" \ -I "$GAMSCAPI" "$GAMSCAPI/gmomcc.c" "$GAMSCAPI/optcc.c" "$GAMSCAPI/gevmcc.c" \ -I "$CUOPT/include" "$JITLINK/libnvJitLink.so.13" \ - -L "$CUOPT/lib64" -lcuopt + -L "$CUOPT/lib64" -lcuopt_mathopt # 5. Patch RPATH patchelf --set-rpath \$ORIGIN gmscuopt-cu13.out @@ -53,15 +53,16 @@ mkdir -p "$GAMSDIST" mv gmscuopt-cu13.out "$GAMSDIST/gmscuopt.out" -cp "$CUOPT/lib64/libcuopt.so" "$GAMSDIST/" -cp "$SITE_PACKAGES"/libraft_cu13.libs/libgomp-*.so* "$GAMSDIST/" +cp "$CUOPT/lib64/libcuopt_mathopt.so" "$GAMSDIST/" +cp "$CUOPT/lib64/libcuopt_client.so" "$GAMSDIST/" +cp "$SITE_PACKAGES"/libcuopt_cu13.libs/libgomp-*.so* "$GAMSDIST/" cp "$SITE_PACKAGES"/libcuopt_cu13.libs/libtbb-*.so* "$GAMSDIST/" cp "$SITE_PACKAGES"/libcuopt_cu13.libs/libtbbmalloc-*.so* "$GAMSDIST/" -cp "$SITE_PACKAGES"/libcuopt_cu13.libs/libomp-*.so* "$GAMSDIST/" +cp "$SITE_PACKAGES"/libcuopt/lib64/libcudss_mtlayer_cuopt.so "$GAMSDIST/" +cp "$SITE_PACKAGES"/libcuopt_cu13.libs/libcudart-*.so* "$GAMSDIST/" cp "$SITE_PACKAGES"/rapids_logger/lib64/librapids_logger.so "$GAMSDIST/" cp "$SITE_PACKAGES"/librmm/lib64/librmm.so "$GAMSDIST/" cp "$SITE_PACKAGES"/nvidia/cu13/lib/libcudss.so* "$GAMSDIST/" -cp "$SITE_PACKAGES"/nvidia/cu13/lib/libcudss_mtlayer_gomp.so* "$GAMSDIST/" cp "$SITE_PACKAGES"/nvidia/cu13/lib/libnvJitLink.so* "$GAMSDIST/" cp "$SITE_PACKAGES"/nvidia/cu13/lib/libcublas.so* "$GAMSDIST/" cp "$SITE_PACKAGES"/nvidia/cu13/lib/libcublasLt.so* "$GAMSDIST/" diff --git a/examples/models/regression_tests/statuses.gms b/examples/models/regression_tests/statuses.gms index 27d1835..6575635 100644 --- a/examples/models/regression_tests/statuses.gms +++ b/examples/models/regression_tests/statuses.gms @@ -40,8 +40,9 @@ abort$(mInfMip.modelstat <> %modelStat.integerInfeasible% and mInfMip.modelstat * --- MIP stopped by the time limit with an incumbent: solve status 3, model status 8 * Market split instance (Cornuejols/Dawande): feasible incumbents are trivial, proving -* optimality is very hard for branch-and-bound. -Set i 'rows' /r1*r4/, j 'columns' /c1*c40/; +* optimality is very hard for branch-and-bound. With n = 10(m-1) columns for m rows, an exact +* split (objective 0) almost never exists, so the solve cannot end early by finding one. +Set i 'rows' /r1*r6/, j 'columns' /c1*c50/; Parameter a(i,j), d(i); option seed = 12345; a(i,j) = uniformInt(0, 99); diff --git a/gmscuopt.c b/gmscuopt.c index ef626bf..eb067d9 100644 --- a/gmscuopt.c +++ b/gmscuopt.c @@ -161,6 +161,8 @@ int main(int argc, char *argv[]) cuopt_float_t* constraint_matrix_coefficent_values=NULL; cuopt_float_t* objective_coefficients=NULL; cuopt_float_t* rhs=NULL; + cuopt_float_t* constraint_lower_bounds=NULL; + cuopt_float_t* constraint_upper_bounds=NULL; cuopt_float_t* lower_bounds=NULL; cuopt_float_t* upper_bounds=NULL; char* constraint_sense=NULL; @@ -246,6 +248,13 @@ int main(int argc, char *argv[]) goto DONE; } } + if (gevGetIntOpt(gev, gevNodeLim) > 0) { // GAMS nodlim=0 means no limit + status = cuOptSetIntegerParameter(settings, CUOPT_NODE_LIMIT, gevGetIntOpt(gev, gevNodeLim)); + if (status != CUOPT_SUCCESS) { + printOut(gev, "Error setting node limit: %d\n", status); + goto DONE; + } + } if (gevGetDblOpt(gev, gevResLim) < RESLIM_INFINITY) { status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, gevGetDblOpt(gev, gevResLim)); if (status != CUOPT_SUCCESS) { @@ -315,8 +324,19 @@ int main(int argc, char *argv[]) printOut(gev, "Error querying method option.\n"); goto DONE; } + cuopt_int_t num_gpus; + status = cuOptGetIntegerParameter(settings, CUOPT_NUM_GPUS, &num_gpus); + if (status != CUOPT_SUCCESS) + { + printOut(gev, "Error querying num_gpus option.\n"); + goto DONE; + } + // multi-GPU PDLP rejects initial solutions + int multigpu_pdlp = chosen_method == CUOPT_METHOD_PDLP && (num_gpus == -1 || num_gpus > 1); + if (multigpu_pdlp && gmoHaveBasis(gmo)) + printOut(gev, "Multi-GPU PDLP does not support initial solutions, ignoring the levels and marginals.\n"); // only when some PDLP is used and we have basis - if ((chosen_method == CUOPT_METHOD_PDLP || chosen_method == CUOPT_METHOD_CONCURRENT) && gmoHaveBasis(gmo)) + else if ((chosen_method == CUOPT_METHOD_PDLP || chosen_method == CUOPT_METHOD_CONCURRENT) && gmoHaveBasis(gmo)) { int nvars = gmoN(gmo), nconstraints = gmoM(gmo); double *lvls = (double *)malloc(sizeof(double) * nvars); @@ -432,6 +452,8 @@ int main(int argc, char *argv[]) constraint_matrix_coefficent_values = malloc(nnz * sizeof(cuopt_float_t)); objective_coefficients = malloc((num_variables) * sizeof(cuopt_float_t)); rhs = malloc((num_linear_constraints) * sizeof(cuopt_float_t)); + constraint_lower_bounds = malloc((num_linear_constraints) * sizeof(cuopt_float_t)); + constraint_upper_bounds = malloc((num_linear_constraints) * sizeof(cuopt_float_t)); lower_bounds = malloc((num_variables) * sizeof(cuopt_float_t)); upper_bounds = malloc((num_variables) * sizeof(cuopt_float_t)); constraint_sense = malloc((num_linear_constraints) * sizeof(char)); @@ -442,6 +464,8 @@ int main(int argc, char *argv[]) (constraint_matrix_coefficent_values == NULL) || (objective_coefficients == NULL) || (rhs == NULL) || + (constraint_lower_bounds == NULL) || + (constraint_upper_bounds == NULL) || (lower_bounds == NULL) || (upper_bounds == NULL) || (constraint_sense == NULL) || @@ -580,6 +604,8 @@ int main(int argc, char *argv[]) constraint_sense[lin_row] = orig_sense[i]; rhs[lin_row] = orig_rhs[i]; + constraint_lower_bounds[lin_row] = (orig_sense[i] == CUOPT_LESS_THAN) ? -CUOPT_INFINITY : orig_rhs[i]; + constraint_upper_bounds[lin_row] = (orig_sense[i] == CUOPT_GREATER_THAN) ? CUOPT_INFINITY : orig_rhs[i]; nnz += rnz; lin_row++; @@ -593,7 +619,9 @@ int main(int argc, char *argv[]) goto DONE; } - status = cuOptCreateProblem( + // Ranged form, since cuOpt's multi-GPU PDLP (without presolve) ignores the row types + RHS + // form of cuOptCreateProblem and sees no constraints + status = cuOptCreateRangedProblem( num_linear_constraints, // Use mapped linear size num_variables, (gmoSense(gmo) == gmoObj_Min) ? CUOPT_MINIMIZE : CUOPT_MAXIMIZE, @@ -602,8 +630,8 @@ int main(int argc, char *argv[]) constraint_matrix_row_offsets, constraint_matrix_column_indices, constraint_matrix_coefficent_values, - constraint_sense, - rhs, + constraint_lower_bounds, + constraint_upper_bounds, lower_bounds, upper_bounds, variable_types, @@ -856,6 +884,23 @@ int main(int argc, char *argv[]) } gmoSetHeadnTail(gmo, gmoHresused, solution_time); + // Iterations and nodes; cuOpt only provides the MIP attributes for MIP solutions and the LP + // attributes for LP solutions (otherwise CUOPT_INVALID_ARGUMENT) + cuopt_int_t nodes = -1; +#ifdef CUOPT_SOLUTION_ATTR_MIP_NUM_NODES + cuopt_int_t iterations = 0; + if (cuOptGetSolutionIntAttribute(solution, CUOPT_SOLUTION_ATTR_MIP_NUM_NODES, &nodes) == CUOPT_SUCCESS) { + gmoSetHeadnTail(gmo, gmoTmipnod, nodes); + if (cuOptGetSolutionIntAttribute(solution, CUOPT_SOLUTION_ATTR_MIP_NUM_SIMPLEX_ITERATIONS, &iterations) == CUOPT_SUCCESS) + gmoSetHeadnTail(gmo, gmoHiterused, iterations); + } + else { + nodes = -1; + if (cuOptGetSolutionIntAttribute(solution, CUOPT_SOLUTION_ATTR_LP_NUM_ITERATIONS, &iterations) == CUOPT_SUCCESS) + gmoSetHeadnTail(gmo, gmoHiterused, iterations); + } +#endif + int is_mip = has_integer_vars && (gmoModelType(gmo) == gmoProc_mip || gmoModelType(gmo) == gmoProc_miqcp); int have_solution = 0; int limit_point = 0; // continuous model stopped by an iteration/time limit @@ -911,7 +956,7 @@ int main(int argc, char *argv[]) // cuOpt does not expose the work units spent, so a set work limit is assumed to be the cause if (solution_time >= 0.99 * time_limit || work_limit < 1e10) gmoSolveStatSet(gmo, gmoSolveStat_Resource); - else if (node_limit < INT32_MAX) + else if (node_limit < INT32_MAX && (nodes < 0 || nodes >= node_limit)) // nodes < 0: unknown gmoSolveStatSet(gmo, gmoSolveStat_Iteration); else gmoSolveStatSet(gmo, gmoSolveStat_Solver); @@ -1035,18 +1080,7 @@ int main(int argc, char *argv[]) final_duals[i] = gams2cuopt_row ? raw_duals[gams2cuopt_row[i]] : raw_duals[i]; cuopt_float_t *reduced_costs = malloc(num_variables * sizeof(cuopt_float_t)); - if (obj_qnz == 0) - { - // cuOpt 26.08's PDLP returns all-zero reduced costs, so compute them for LPs from the - // duals as d = c - A^T y (exact for dual simplex and barrier as well). GMO's own - // computation (gmoSetSolution2) ignores c when the objective variable is eliminated. - status = gmoGetObjVector(gmo, reduced_costs, NULL); - for (int r = 0; r < num_constraints && !status; r++) - for (int k = constraint_matrix_row_offsets[r]; k < constraint_matrix_row_offsets[r + 1]; k++) - reduced_costs[constraint_matrix_column_indices[k]] -= constraint_matrix_coefficent_values[k] * raw_duals[r]; - } - else - status = cuOptGetReducedCosts(solution, reduced_costs); + status = cuOptGetReducedCosts(solution, reduced_costs); if (status) { printOut(gev, "Error getting reduced costs: %d\n", status); @@ -1116,6 +1150,8 @@ int main(int argc, char *argv[]) free(constraint_matrix_coefficent_values); free(objective_coefficients); free(rhs); + free(constraint_lower_bounds); + free(constraint_upper_bounds); free(lower_bounds); free(upper_bounds); free(constraint_sense); diff --git a/tests/baseline.txt b/tests/baseline.txt index c3509d2..c0f59a7 100644 --- a/tests/baseline.txt +++ b/tests/baseline.txt @@ -42,7 +42,7 @@ pak LP 1075.5471391775 paklive LP 19468.0876969476 pdi LP 294070 port LP 0.298363636363636 -poutil MIP 266793 +poutil MIP 266793 300 prodmix LP 18666.6666666667 prodsch MIP 3254.02381132426 prodsp LP 17869.0449355452 diff --git a/tests/test-gamslib-cuopt.py b/tests/test-gamslib-cuopt.py index 924050a..c3cb1b4 100644 --- a/tests/test-gamslib-cuopt.py +++ b/tests/test-gamslib-cuopt.py @@ -1,8 +1,9 @@ """Regression test: solve gamslib models with cuOpt and compare against CPLEX. -Each model listed in baseline.txt (name, model type, CPLEX objective) is -extracted with `gamslib`, solved with `solver=cuopt`, and the objective value -reported in the GAMS trace file is compared against the CPLEX reference. +Each model listed in baseline.txt (name, model type, CPLEX objective and optionally a +model-specific time limit in seconds that replaces --reslim) is extracted with `gamslib`, +solved with `solver=cuopt`, and the objective value reported in the GAMS trace file is +compared against the CPLEX reference. Run with: python3 tests/test-gamslib-cuopt.py [-g GAMS_DIR] [-j JOBS] [model ...] @@ -47,13 +48,14 @@ def rel_diff(self) -> float | None: return abs(self.obj - self.ref) / max(1.0, abs(self.ref)) -def read_baseline(path: str) -> list[tuple[str, str, float]]: +def read_baseline(path: str) -> list[tuple[str, str, float, int | None]]: entries = [] with open(path) as f: for line in f: fields = line.split() - if len(fields) == 3: - entries.append((fields[0], fields[1], float(fields[2]))) + if len(fields) in (3, 4): + reslim = int(fields[3]) if len(fields) == 4 else None + entries.append((fields[0], fields[1], float(fields[2]), reslim)) return entries @@ -130,8 +132,6 @@ def main() -> int: entries = [e for e in entries if e[0] in args.models] work_dir = tempfile.mkdtemp(prefix="gamslib-cuopt-") - # Some models set their own resLim, so leave generous headroom beyond --reslim. - timeout = 2 * args.reslim + 60 row = "{:6} {:12} {:6} {:>9} {:>9} {:>9} {:>17} {:>17} {}" print(f"cuOpt vs. CPLEX on {len(entries)} gamslib models " @@ -142,8 +142,11 @@ def main() -> int: print("-" * len(header), flush=True) def job(entry): - name, model_type, ref = entry - res = run_model(gams_dir, work_dir, name, model_type, ref, args.reslim, timeout) + name, model_type, ref, model_reslim = entry + reslim = model_reslim or args.reslim + # Some models set their own resLim, so leave generous headroom beyond reslim. + timeout = 2 * reslim + 60 + res = run_model(gams_dir, work_dir, name, model_type, ref, reslim, timeout) rel = res.rel_diff() ok = res.error is None and rel is not None and rel <= args.rtol obj = "NA" if res.obj is None else f"{res.obj:.10g}"