diff --git a/.github/workflows/main-arm64.yml b/.github/workflows/main-arm64.yml new file mode 100644 index 0000000..0486404 --- /dev/null +++ b/.github/workflows/main-arm64.yml @@ -0,0 +1,162 @@ +name: Build cuOpt link for GAMS (ARM64) + +on: + push: +# branches: [main] + tags: + - '*' + pull_request: + workflow_dispatch: + +jobs: + build-link: + runs-on: ubuntu-24.04-arm + container: + image: python:3.12 + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install dependencies + run: | + apt-get update && apt-get install -y patchelf curl unzip zip gcc + + # Get CUDA runtimes via pip + - name: Set up python virtual environments with NVIDIA dependencies for CUDA 12 and CUDA 13 (respectively) + run: | + mkdir -p venvs + python -m venv venvs/cu12 + bash -c "source venvs/cu12/bin/activate && \ + pip install --upgrade pip -qq && \ + pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12==25.12.* -qq && + deactivate" + python -m venv venvs/cu13 + bash -c "source venvs/cu13/bin/activate && \ + pip install --upgrade pip -qq && \ + pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu13==25.12.* -qq && + deactivate" + + # Get GAMS (ARM64 version) + - name: Download and extract latest GAMS distribution + run: | + curl https://d37drm4t2jghv5.cloudfront.net/distributions/latest/linux/linux_arm64_sfx.exe --output linux_arm64_sfx.exe + unzip -q linux_arm64_sfx.exe + mv gams*_linux_arm64_sfx gamsdist + rm linux_arm64_sfx.exe + + # Build link + - name: Compile GAMS/cuOpt-link binary "gmscuopt.out" for CUDA 12 and then for CUDA 13 + run: | + export GAMSCAPI="gamsdist/apifiles/C/api" + export CUOPT="venvs/cu12/lib/python3.12/site-packages/libcuopt" + export JITLINK="venvs/cu12/lib/python3.12/site-packages/nvidia/nvjitlink/lib" + export CUOPT_VERSION="`cat "$CUOPT/VERSION"`" + export CUOPT_HASH="`cat "$CUOPT/GIT_COMMIT"`" + 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 + patchelf --set-rpath \$ORIGIN gmscuopt-cu12.out + export CUOPT="venvs/cu13/lib/python3.12/site-packages/libcuopt" + export JITLINK="venvs/cu13/lib/python3.12/site-packages/nvidia/cu13/lib" + export CUOPT_VERSION="`cat "$CUOPT/VERSION"`" + export CUOPT_HASH="`cat "$CUOPT/GIT_COMMIT"`" + 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 + patchelf --set-rpath \$ORIGIN gmscuopt-cu13.out + + # Collect dependencies for link and runtime convenience archive + - name: Prepare release artifact and runtime bundle + run: | + mkdir release-cu12 + cp gmscuopt-cu12.out release-cu12/gmscuopt.out + cp assets/* release-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/libcuopt/lib64/libcuopt.so release-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/libcuopt/lib64/libmps_parser.so release-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/libcuopt_cu12.libs/libgomp-*.so.1.0.0 release-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/libcuopt_cu12.libs/libtbb-*.so.2 release-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/libcuopt_cu12.libs/libtbbmalloc-*.so.2 release-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/rapids_logger/lib64/librapids_logger.so release-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/librmm/lib64/librmm.so release-cu12/ + mkdir runtime-cu12 + cp venvs/cu12/lib/python3.12/site-packages/nvidia/cu12/lib/libcudss.so.0 runtime-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/nvidia/cu12/lib/libcudss_mtlayer_gomp.so.0 runtime-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/nvidia/cusolver/lib/libcusolver.so.11 runtime-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/nvidia/cublas/lib/libcublas.so.12 runtime-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/nvidia/cublas/lib/libcublasLt.so.12 runtime-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/nvidia/nvjitlink/lib/libnvJitLink.so.12 runtime-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/nvidia/curand/lib/libcurand.so.10 runtime-cu12/ + cp venvs/cu12/lib/python3.12/site-packages/nvidia/cusparse/lib/libcusparse.so.12 runtime-cu12/ + mkdir release-cu13 + cp gmscuopt-cu13.out release-cu13/gmscuopt.out + cp assets/* release-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/libcuopt/lib64/libcuopt.so release-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/libcuopt/lib64/libmps_parser.so release-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/libcuopt_cu13.libs/libgomp-*.so.1.0.0 release-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/libcuopt_cu13.libs/libtbb-*.so.2 release-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/libcuopt_cu13.libs/libtbbmalloc-*.so.2 release-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/rapids_logger/lib64/librapids_logger.so release-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/librmm/lib64/librmm.so release-cu13/ + mkdir runtime-cu13 + cp venvs/cu13/lib/python3.12/site-packages/nvidia/cu13/lib/libcudss.so.0 runtime-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/nvidia/cu13/lib/libcudss_mtlayer_gomp.so.0 runtime-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/nvidia/cu13/lib/libnvJitLink.so.13 runtime-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/nvidia/cu13/lib/libcublas.so.13 runtime-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/nvidia/cu13/lib/libcublasLt.so.13 runtime-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/nvidia/cu13/lib/libcurand.so.10 runtime-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/nvidia/cu13/lib/libcusolver.so.12 runtime-cu13/ + cp venvs/cu13/lib/python3.12/site-packages/nvidia/cu13/lib/libcusparse.so.12 runtime-cu13/ + + # Upload artifacts + - name: Upload CUDA 12 link artifact to GitHub Actions (always) + uses: actions/upload-artifact@v4 + with: + name: cuopt-link-cu12-arm64 + path: release-cu12/ + + - name: Upload CUDA 12 runtime artifact to GitHub Actions (always) + uses: actions/upload-artifact@v4 + with: + name: cu12-runtime-arm64 + path: runtime-cu12/ + + - name: Upload CUDA 13 link artifact to GitHub Actions (always) + uses: actions/upload-artifact@v4 + with: + name: cuopt-link-cu13-arm64 + path: release-cu13/ + + - name: Upload CUDA 13 runtime artifact to GitHub Actions (always) + uses: actions/upload-artifact@v4 + with: + name: cu13-runtime-arm64 + path: runtime-cu13/ + + # Zip Files + - name: Create zip archive (only on tag push) + if: startsWith(github.ref, 'refs/tags/') + run: | + cd release-cu12 + zip -r ../cuopt-link-release-cu12-arm64.zip . + cd ../runtime-cu12 + zip -r ../cu12-runtime-arm64.zip . + cd ../release-cu13 + zip -r ../cuopt-link-release-cu13-arm64.zip . + cd ../runtime-cu13 + zip -r ../cu13-runtime-arm64.zip . + + # Create new release with archives + - name: Create GitHub Release (only on tag push) + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + with: + files: | + cuopt-link-release-cu12-arm64.zip + cu12-runtime-arm64.zip + cuopt-link-release-cu13-arm64.zip + cu13-runtime-arm64.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main-x86_64.yml similarity index 93% rename from .github/workflows/main.yml rename to .github/workflows/main-x86_64.yml index b9bb860..4a157a1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main-x86_64.yml @@ -1,4 +1,4 @@ -name: Build cuOpt link for GAMS +name: Build cuOpt link for GAMS (x86_64) on: push: @@ -6,6 +6,7 @@ on: tags: - '*' # Run only when a new tag is pushed pull_request: + workflow_dispatch: jobs: build-link: @@ -28,12 +29,12 @@ jobs: python -m venv venvs/cu12 bash -c "source venvs/cu12/bin/activate && \ pip install --upgrade pip -qq && \ - pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12==25.10.* cuda-toolkit[cudart,nvjitlink]==12.9.* -qq && + pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12==25.12.* -qq && deactivate" python -m venv venvs/cu13 bash -c "source venvs/cu13/bin/activate && \ pip install --upgrade pip -qq && \ - pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu13==25.10.* cuda-toolkit[cudart,nvjitlink]==13.0.* -qq && + pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu13==25.12.* -qq && deactivate" # Get GAMS @@ -139,13 +140,13 @@ jobs: if: startsWith(github.ref, 'refs/tags/') run: | cd release-cu12 - zip -r ../cuopt-link-release-cu12.zip . + zip -r ../cuopt-link-release-cu12-x86_64.zip . cd ../runtime-cu12 - zip -r ../cu12-runtime.zip . + zip -r ../cu12-runtime-x86_64.zip . cd ../release-cu13 - zip -r ../cuopt-link-release-cu13.zip . + zip -r ../cuopt-link-release-cu13-x86_64.zip . cd ../runtime-cu13 - zip -r ../cu13-runtime.zip . + zip -r ../cu13-runtime-x86_64.zip . # Create new release with archives - name: Create GitHub Release (only on tag push) @@ -153,9 +154,9 @@ jobs: uses: softprops/action-gh-release@v2 with: files: | - cuopt-link-release-cu12.zip - cu12-runtime.zip - cuopt-link-release-cu13.zip - cu13-runtime.zip + cuopt-link-release-cu12-x86_64.zip + cu12-runtime-x86_64.zip + cuopt-link-release-cu13-x86_64.zip + cu13-runtime-x86_64.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 76a450c..4924155 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ You can get more details and tips by reading the blog post ["GPU-Accelerated Opt ## Requirements - **Operating System:** Linux, Windows 11 through WSL2 +- **CPU architecture:** x86_64, arm64 - **GAMS:** Version 49 or newer - **GAMSPy:** Version 1.12.1 or newer - **NVIDIA GPU:** Volta architecture or better @@ -17,11 +18,11 @@ You can get more details and tips by reading the blog post ["GPU-Accelerated Opt ## Getting started / installation - Make sure [CUDA runtime](https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64) is installed -- Download and unpack `cuopt-link-release-cu12.zip` or `cuopt-link-release-cu13.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)`. +- Download and unpack `cuopt-link-release-cu12.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*-{x86_64,arm64}.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 neccessary files from the CUDA 12 or 13 runtime can also be downloaded as convenient archive `cu12-runtime.zip` or `cu13-runtime.zip` from the [releases page](https://github.com/GAMS-dev/cuoptlink-builder/releases). +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). ## Test the setup @@ -33,5 +34,5 @@ gams trnsport lp cuopt ## Examples -- [examples/trnsport_cuopt.ipynb](examples/trnsport_cuopt.ipynb) for CUDA 12 -- [examples/trnsport_cuopt.ipynb](examples/trnsport_cuopt_cu13.ipynb) for CUDA 13 +- [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 diff --git a/assets/optcuopt.def b/assets/optcuopt.def index 8a5d009..0d91c9f 100644 --- a/assets/optcuopt.def +++ b/assets/optcuopt.def @@ -2,6 +2,7 @@ * optcuopt.def * 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. presolve boolean 0 0 1 1 Controls whether presolve is enabled. Presolve can reduce problem size and improve solve time. Enabled by default for MIP, disabled by default for LP. dual_postsolve boolean 0 0 1 2 Controls whether dual postsolve is enabled. Disabling dual postsolve can improve solve time at the expense of not having access to the dual solution. Enabled by default for LP when presolve is enabled. This is not relevant for MIP problems time_limit integer 0 maxint 0 maxint 1 1 Controls the time limit in seconds after which the solver will stop and return the current solution (default GAMS ResLim) @@ -54,6 +55,7 @@ absolute_gap_tolerance double 0 0.0001 0 maxdouble 1 2 Controls the absolute gap relative_gap_tolerance double 0 0.0001 0 maxdouble 1 2 Controls the relative gap tolerance used in PDLP's duality gap check primal_infeasible_tolerance double 0 1e-08 0 maxdouble 0 2 Unknown dual_infeasible_tolerance double 0 1e-08 0 maxdouble 0 2 Unknown +miptrace string 0 "" 1 1 filename of MIP trace file mip_heuristics_only boolean 0 0 1 3 Controls if only the GPU heuristics should be run mip_scaling boolean 0 1 1 3 Controls if scaling should be applied to the MIP problem mip_absolute_tolerance double 0 0.0001 0 maxdouble 1 3 Controls the MIP absolute tolerance diff --git a/examples/trnsport_cuopt.ipynb b/examples/trnsport_cuopt.ipynb index 60cee67..f0bf1c6 100644 --- a/examples/trnsport_cuopt.ipynb +++ b/examples/trnsport_cuopt.ipynb @@ -40,8 +40,8 @@ "import sys\n", "!pip install -q gamspy\n", "gams_base_path = subprocess.check_output([sys.executable, '-m', 'gamspy', 'show', 'base']).decode('utf-8').strip()\n", - "!wget -nc -nv --show-progress -q \"https://github.com/GAMS-dev/cuoptlink-builder/releases/latest/download/cu12-runtime.zip\"\n", - "!wget -nc -nv --show-progress -q \"https://github.com/GAMS-dev/cuoptlink-builder/releases/latest/download/cuopt-link-release-cu12.zip\"\n", + "!wget -nc -nv --show-progress -q \"https://github.com/GAMS-dev/cuoptlink-builder/releases/latest/download/cu12-runtime-x86_64.zip\"\n", + "!wget -nc -nv --show-progress -q \"https://github.com/GAMS-dev/cuoptlink-builder/releases/latest/download/cuopt-link-release-cu12-x86_64.zip\"\n", "subprocess.run(f\"unzip -q -o cu12-runtime.zip -d {gams_base_path}\", shell=True, check=True)\n", "subprocess.run(f\"unzip -q -o cuopt-link-release-cu12.zip -d {gams_base_path}\", shell=True, check=True)" ] diff --git a/examples/trnsport_cuopt_cu13.ipynb b/examples/trnsport_cuopt_cu13.ipynb index 9cb4648..713b64e 100644 --- a/examples/trnsport_cuopt_cu13.ipynb +++ b/examples/trnsport_cuopt_cu13.ipynb @@ -32,8 +32,8 @@ "import sys\n", "!pip install -q gamspy\n", "gams_base_path = subprocess.check_output([sys.executable, '-m', 'gamspy', 'show', 'base']).decode('utf-8').strip()\n", - "!wget -nc -nv --show-progress -q \"https://github.com/GAMS-dev/cuoptlink-builder/releases/latest/download/cu13-runtime.zip\"\n", - "!wget -nc -nv --show-progress -q \"https://github.com/GAMS-dev/cuoptlink-builder/releases/latest/download/cuopt-link-release-cu13.zip\"\n", + "!wget -nc -nv --show-progress -q \"https://github.com/GAMS-dev/cuoptlink-builder/releases/latest/download/cu13-runtime-x86_64.zip\"\n", + "!wget -nc -nv --show-progress -q \"https://github.com/GAMS-dev/cuoptlink-builder/releases/latest/download/cuopt-link-release-cu13-x86_64.zip\"\n", "subprocess.run(f\"unzip -q -o cu13-runtime.zip -d {gams_base_path}\", shell=True, check=True)\n", "subprocess.run(f\"unzip -q -o cuopt-link-release-cu13.zip -d {gams_base_path}\", shell=True, check=True)" ] diff --git a/gmscuopt.c b/gmscuopt.c index 113395b..dff88a6 100644 --- a/gmscuopt.c +++ b/gmscuopt.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "gmomcc.h" #include "gevmcc.h" #include "optcc.h" @@ -23,8 +24,26 @@ printOut (gevHandle_t gev, char *fmt, ...) return rc; } -int -main (int argc, char *argv[]) +static char fln_mip_trace[256]; +static char mip_trace_id[32] = ""; +static FILE *fp_mip_trace = NULL; +static int mip_trace_seq = 0; + +static int mip_trace_open(const char *fname, const char *solverID, const int optFileNum, const char *inputName); +static int mip_trace_close(); +static int mip_trace_line(char seriesID, double node, int giveint, double seconds, double bestint, double bestbnd); + +typedef struct sl_state_s +{ + gevHandle_t gev; + double tstart; + int nvars; +} sl_state_t; + +static void mip_get_solution_cb(const cuopt_float_t *solution, const cuopt_float_t *objective_value, + const cuopt_float_t *solution_bound, void *user_data); + +int main(int argc, char *argv[]) { gmoHandle_t gmo=NULL; gevHandle_t gev=NULL; @@ -125,6 +144,9 @@ main (int argc, char *argv[]) cuopt_float_t* upper_bounds=NULL; char* constraint_sense=NULL; char* variable_types=NULL; + int has_integer_vars = 0; + fln_mip_trace[0] = '\0'; + sl_state_t context; // Create solver settings status = cuOptCreateSolverSettings(&settings); @@ -133,8 +155,24 @@ main (int argc, char *argv[]) goto DONE; } + if (fp_mip_trace) + { + context.gev = gev; + context.tstart = gevTimeJNow(gev); + context.nvars = num_variables; + mip_trace_line('S', 0, 0, 0, GMS_SV_NA, GMS_SV_NA); + /* + status = cuOptSetMIPGetSolutionCallback(settings, mip_get_solution_cb, &context); + if (status != CUOPT_SUCCESS) { + printOut(gev, "Error setting get-solution callback\n", status); + goto DONE; + } + */ + } + // Set solver parameters with GAMS options - if (gevGetIntOpt(gev, gevThreadsRaw) != 0) { + if (gevGetIntOpt(gev, gevThreadsRaw) != 0) + { status = cuOptSetIntegerParameter(settings, CUOPT_NUM_CPU_THREADS, gevGetIntOpt(gev, gevThreadsRaw)); if (status != CUOPT_SUCCESS) { printOut(gev, "Error setting number of CPU threads: %d\n", status); @@ -192,7 +230,13 @@ main (int argc, char *argv[]) } } } - + + if(optGetDefinedStr(opt, "mip_trace_")) { + optGetStrStr(opt, "mip_trace_", fln_mip_trace); + char sval2[256]; + mip_trace_open(fln_mip_trace, "cuOpt", gmoOptFile(gmo), gmoNameInput(gmo, sval2)); + } + if (!optGetDefinedStr(opt, "prob_read")) { constraint_matrix_row_offsets = malloc((num_constraints+1)*sizeof(cuopt_int_t)); constraint_matrix_column_indices = malloc(nnz*sizeof(cuopt_int_t)); @@ -242,7 +286,10 @@ main (int argc, char *argv[]) switch (constraint_matrix_column_indices[j]) { case gmovar_X: variable_types[j] = CUOPT_CONTINUOUS; break; case gmovar_B: - case gmovar_I: variable_types[j] = CUOPT_INTEGER; break; + case gmovar_I: + variable_types[j] = CUOPT_INTEGER; + has_integer_vars = 1; + break; default: printOut(gev, "Known variable type %d\n", constraint_matrix_column_indices[j]); } } @@ -348,7 +395,7 @@ main (int argc, char *argv[]) // Get solution information cuopt_float_t solution_time; cuopt_int_t termination_status; - cuopt_float_t objective_value; + cuopt_float_t objective_value, solution_bound; status = cuOptGetTerminationStatus(solution, &termination_status); if (status != CUOPT_SUCCESS) { @@ -379,14 +426,14 @@ main (int argc, char *argv[]) gmoSolveStatSet(gmo, gmoSolveStat_SolverErr); break; case CUOPT_TERIMINATION_STATUS_PRIMAL_FEASIBLE: - if (gmoModelType(gmo) == gmoProc_mip) { + if (gmoModelType(gmo) == gmoProc_mip && has_integer_vars) { gmoModelStatSet(gmo, gmoModelStat_Integer); } else { gmoModelStatSet(gmo, gmoModelStat_Feasible); } break; case CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND: - if (gmoModelType(gmo) == gmoProc_mip) { + if (gmoModelType(gmo) == gmoProc_mip && has_integer_vars) { gmoModelStatSet(gmo, gmoModelStat_Integer); } else { gmoModelStatSet(gmo, gmoModelStat_Feasible); @@ -412,13 +459,13 @@ main (int argc, char *argv[]) } gmoSetHeadnTail(gmo, gmoHobjval, objective_value); - if (gmoModelType(gmo) == gmoProc_mip) { - status = cuOptGetSolutionBound(solution, &objective_value); + if (gmoModelType(gmo) == gmoProc_mip && has_integer_vars) { + status = cuOptGetSolutionBound(solution, &solution_bound); if (status != CUOPT_SUCCESS) { printOut(gev, "Error getting solution bound: %d\n", status); goto DONE; } - gmoSetHeadnTail(gmo, gmoTmipbest, objective_value); + gmoSetHeadnTail(gmo, gmoTmipbest, solution_bound); } status = cuOptGetPrimalSolution(solution, objective_coefficients); // reuse n-vector @@ -428,10 +475,16 @@ main (int argc, char *argv[]) } gmoSetVarL(gmo, objective_coefficients); + if(fp_mip_trace) + { + double total_elapsed = (gevTimeJNow(gev) - context.tstart) * 3600.0 * 24.0; + mip_trace_line('E', 0, 1, total_elapsed, objective_value, solution_bound); + } + int presolve, dual_postsolve; cuOptGetIntegerParameter(settings, "presolve", &presolve); cuOptGetIntegerParameter(settings, "dual_postsolve", &dual_postsolve); - if (gmoModelType(gmo) != gmoProc_mip && (!presolve || dual_postsolve) ) { + if (gmoModelType(gmo) != gmoProc_mip && !has_integer_vars && (!presolve || dual_postsolve) ) { status = cuOptGetReducedCosts(solution, objective_coefficients); // reuse n-vector if (status != CUOPT_SUCCESS) { printOut(gev, "Error getting reduced cost: %d\n", status); @@ -480,6 +533,9 @@ main (int argc, char *argv[]) free(constraint_sense); free(variable_types); + if(fp_mip_trace) + mip_trace_close(); + GAMSDONE: gmoFree(&gmo); gevFree(&gev); @@ -489,6 +545,81 @@ main (int argc, char *argv[]) } /* main */ +int mip_trace_open(const char *fname, const char *solverID, const int optFileNum, const char *inputName) +{ + if (NULL != fp_mip_trace) + return 1; /* already open: error */ + + strcpy(fln_mip_trace, fname); + fp_mip_trace = fopen(fln_mip_trace, "w"); + if (NULL == fp_mip_trace) + return 3; + + strncpy(mip_trace_id, solverID, sizeof(mip_trace_id) - 1); + mip_trace_id[sizeof(mip_trace_id) - 1] = '\0'; + mip_trace_seq = 1; + fprintf(fp_mip_trace, "* mip_trace_ file %s: ID = %s.%d Instance = %s\n", fln_mip_trace, mip_trace_id, optFileNum, inputName); + fprintf(fp_mip_trace, "* fields are lineNum, seriesID, node, seconds, bestFound, bestBound\n"); + fflush(fp_mip_trace); + return 0; +} /* mip_trace_open */ + +int mip_trace_close() +{ + int rc; + if (NULL == fp_mip_trace) + return 2; /* already closed: error */ + fprintf(fp_mip_trace, "* mip_trace_ file %s closed\n", fln_mip_trace); + rc = fclose(fp_mip_trace); + fp_mip_trace = NULL; + return (0 == rc) ? 0 : 1; +} /* mip_trace_close */ + +int mip_trace_line(char seriesID, double node, int giveint, + double seconds, double bestint, double bestbnd) +{ + int rc; + + if (NULL == fp_mip_trace) + return -1; /* not open: error */ + + if (giveint) + { + if (bestbnd == GMS_SV_NA) + rc = fprintf(fp_mip_trace, "%d, %c, %g, %.15g, %.15g, na\n", mip_trace_seq, + isalnum(seriesID) ? seriesID : 'X', + node, seconds, bestint); + else + rc = fprintf(fp_mip_trace, "%d, %c, %g, %.15g, %.15g, %.15g\n", mip_trace_seq, + isalnum(seriesID) ? seriesID : 'X', + node, seconds, bestint, bestbnd); + } + else + { + if (bestbnd == GMS_SV_NA) + rc = fprintf(fp_mip_trace, "%d, %c, %g, %.15g, na, na\n", mip_trace_seq, + isalnum(seriesID) ? seriesID : 'X', + node, seconds); + else + rc = fprintf(fp_mip_trace, "%d, %c, %g, %.15g, na, %g\n", mip_trace_seq, + isalnum(seriesID) ? seriesID : 'X', + node, seconds, bestbnd); + } + fflush(fp_mip_trace); + mip_trace_seq++; + + return rc; +} /* mip_trace_line */ + +static void mip_get_solution_cb(const cuopt_float_t *solution, const cuopt_float_t *objective_value, + const cuopt_float_t *solution_bound, void *user_data){ + sl_state_t *state = (sl_state_t *)user_data; + double elapsed = (gevTimeJNow(state->gev) - state->tstart) * 3600.0 * 24.0; + double obj = *objective_value; + double bnd = *solution_bound; + mip_trace_line('I', 0, 1, elapsed, obj, bnd); +} + #if 0 t program for cuOpt linear programming solver */