A Docker image for PIPS-IPM++ is available in the container registry registry.gitlab.com/pips-ipmpp/pips-ipmpp. Based on debian:stable, it includes a pre-built version of PIPS-IPM++ located in /pips-ipmpp/build and a Python-based controller to orchestrate complex decomposition workflows. The build specifically utilizes the MUMPS linear system solver.
The PIPS-IPM++ Docker image provides a specialized environment for annotating, splitting, and solving large-scale linear programming instances. The container offers users two primary interfaces: a command-line interface (CLI) for direct automation and a stateful interactive shell for step-by-step session management.
Starting the Container
To use the PIPS-IPM++ Docker container effectively, you must mount a local directory to the container so that the application can access your problem files and save results.
- Create a Working Directory: On your host machine, create a directory (e.g., /path/to/workdir) and place your instance files (.mps, .dec, or .gdx) there.
- Options Files: If specific solver options are required, place a file named PIPSIPMpp.opt in this directory.
- Run Command: Use the -v flag to mount your local path to /workdir inside the container and the -w flag to set it as the active working directory.
Basic Syntax:
docker run -v /path/to/workdir:/workdir -w /workdir -it pips-ipmpp [command]
Without a command, the container defaults to starting the Interactive Shell. The available commands are interactive and cli.
Key Features
The CLI and interactive shell of the image both provide three primary features to manage the PIPS-IPM++ pipeline:
- Annotate: Generates an annotated GDX file required for decomposition. It supports manual block definition via a .dec file (using GAMS mps2gms) or automatic block detection via pipstools.
- Split: Partitions an annotated GDX file into individual block files using the gmschk utility, which is necessary for parallel solving. The split command generates a separate GDX file for each block.
- Solve: Orchestrates the solution process using gmspips. It can handle individual stages or an automated "full pipeline" that handles annotation and splitting in one step.
Input types
Three different input types are accepted by the container: MPS (and MPS.GZ), GDX and a file stem. In order for PIPS-IPM++ to read the instance, it must have a block structure annotation. The annotation can be performed automatically or, in the case of an MPS file, given by supplying a DEC file. An annotated GDX file can be solved directly using PIPS-IPM++. All annotated GDX files can be split into separate GDX files per block. When solving the split problem, the solve command must be supplied the file stem.
Interactive Shell
The interactive shell provides a stateful environment that remembers your loaded files and block counts across commands.
Launch:
docker run -v /path/to/workdir:/workdir -w /workdir -it pips-ipmpp interactive
Shell Commands
- read <file> [dec_file]: Loads your primary problem file (.mps, .mps.gz, .lp, .lp.gz, or .gdx), an optional decomposition file (.dec) or a solution file (.sol).
- blocks <num>: Manually sets or overrides the number of blocks for the session.
- annotate [args]: Annotates the loaded file. If a .dec is present, it uses GAMS; otherwise, it calls pipstools.
- split [--blocks <num>]: Partitions the file into the specified number of blocks.
- solve [options]: Solves the problem. It automatically uses the session's current file, block count, and .dec file unless overridden.
- solution [value, dual] [<varname> ..] [<consname> ..]: Prints either the read solution, or the solution at the end of the solve. This can be filtered by primal and dual solutions and by variable and constraints.
Quick Shell Examples
| Action | Interactive Shell Commands |
| Annotate a GDX with 10 blocks | read prob.gdx → blocks 10 → annotate |
| Split a GDX with 10 blocks | read prob.gdx → blocks 10 → split |
| Solve a GDX with 10 blocks | read prob.gdx → blocks 10 → solve |
| Solve from a filestem (10 blocks) | read prob_stem → solve --blocks 10 |
| Annotate an MPS with 5 blocks | read model.mps → annotate --blocks 5 (Result: model_5b.gdx) |
| Annotate using a DEC file | read model.mps model.dec → annotate (Result: annotated GDX file) |
| Print the final solution | read prob.gdx → blocks 10 → solve → solution |
| Print the values of the final solution | read prob.gdx → blocks 10 → solve → solution values |
Typical work flow
An instance is provided as an MPS file small.mps
(pips-ipm++) > read small.mps
Successfully read input file: /workdir/small.mps
(pips-ipm++) > blocks 2
(pips-ipm++) > annotate
Executing: cli annotate /workdir/small.mps --blocks 2
Annotation successful. Output: /workdir/small_2b.gdx, Scenarios: 2
(pips-ipm++) > solve
Executing: cli solve /workdir/small_2b.gdx --blocks 2
If a DEC file is available
(pips-ipm++) > read small.mps small.dec
Successfully read input file: /workdir/small.mps
Successfully read dec file: /workdir/small.dec
(pips-ipm++) > annotate
Executing: cli annotate /workdir/small.mps --dec-file /wordir/small.dec
Annotation successful. Output: /workdir/small_2b.gdx, Scenarios: 2
(pips-ipm++) > solve
Executing: cli solve /workdir/small_2b.gdx --blocks 2
Command Line Interface (CLI)
The CLI is designed for non-interactive automation and direct commands.
CLI Commands
- cli annotate <file> [options]: Runs the annotation stage. All options are passed to pipstools.
- cli split <gdx_file> --blocks <num>: Splits a GDX into num blocks.
- cli solve <input> --blocks <num> [options]: The primary entry point for solving. All options are passed to gmspips.
CLI Examples
| Action | CLI Command |
| Annotate a GDX with 10 blocks | cli annotate prob.gdx --blocks 10 |
| Split a GDX with 10 blocks | cli split prob_10b.gdx --blocks 10 |
| Solve a GDX with 10 blocks | cli solve prob_10b.gdx --blocks 10 |
| Solve from a filestem (10 blocks) | cli solve prob_stem --blocks 10 |
| Annotate an MPS with 5 blocks | cli annotate model.mps --blocks 5 |
| Annotate an MPS using a DEC file | cli annotate model.mps --dec-file model.dec |
Comprehensive Solve Workflow
The solve command can automate different segments of the PIPS-IPM++ pipeline based on the input provided.
| Input Type | Command Variant | Action Taken |
| Filestem | cli solve stem --blocks 10 | Directly solves pre-split GDX files (stem0.gdx...stem9.gdx). |
| GDX | cli solve prob.gdx --blocks 10 | Attempts to solve the unsplit GDX file. |
| GDX + Split | cli solve prob.gdx --blocks 10 --split | Forces a split step on the GDX before solving. |
| GDX + Full | cli solve prob.gdx --blocks 10 --annotate --split | Re-annotates, splits, and then solves. |
| MPS | cli solve model.mps --blocks 10 | Automatically performs annotate → split → solve. |
| MPS + DEC | cli solve model.mps --dec-file model.dec | Annotates via DEC file, then splits and solves. |
Note: All commands above support the --mpi <n> flag for parallel execution and --hsl-lib <path> for HSL solvers.
Using HSL Linear Solvers
To use HSL solvers (e.g., MA57), you must provide a library named libhsl.so (libcoinhsl.so for v0.1.0; see also option HSLLIB). Mount the library and use the --hsl-lib flag:
docker run -v /path/to/hsl/lib:/libs -v /path/to/workdir:/workdir -w /workdir -it pips-ipmpp cli solve model.mps --blocks 10 --hsl-lib /libs
Required PIPSIPMpp.opt settings for MA57:
LINEAR_LEAF_SOLVER ma57
LINEAR_ROOT_SOLVER ma57
LINEAR_SUB_ROOT_SOLVER ma57
Using PIPS-IPM++ Directly
Advanced users can bypass the Python controller and call the PIPS-IPM++ binaries directly:
gmschk
docker run -v /path/to/workdir:/workdir -w /workdir pips-ipmpp gmschk 7 annot.gdx
gmspips
docker run -v /path/to/workdir:/workdir -w /workdir pips-ipmpp gmspips 7 annot.gdx
pipstools
Access pipstools directly for advanced annotation and model analysis: docker run pips-ipmpp pipstools --help
More Concrete Workflow Examples
Retrieve the Docker image
Docker:
docker pull registry.gitlab.com/pips-ipmpp/pips-ipmpp/pips-ipmpp:latest
(depending on the system setup, use sudo for this and following docker calls)
Podman:
podman pull registry.gitlab.com/pips-ipmpp/pips-ipmpp/pips-ipmpp:latest
Apptainer:
apptainer pull docker://registry.gitlab.com/pips-ipmpp/pips-ipmpp/pips-ipmpp
Run on MPS and DEC input files
Retrieve some sample data (mini.mps, mini.dec):
mkdir workdir
curl -LO --output-dir workdir https://pips-ipmpp.gitlab.io/mini.mps
curl -LO --output-dir workdir https://pips-ipmpp.gitlab.io/mini.dec
Start a container with the interactive shell:
docker run -v $PWD/workdir:/workdir -w /workdir -it registry.gitlab.com/pips-ipmpp/pips-ipmpp/pips-ipmpp:latest
(replace docker by podman for Podman; use apptainer run pips-ipmpp_latest.sif for Apptainer)
Read the sample, annotate and split it, and then solve it:
read mini.mps mini.dec
annotate
split
solve --mpi 5
quit
The read step should produce following output:
(pips-ipm++) > read mini.mps mini.dec
Successfully read input file: /workdir/mini.mps
Successfully read dec file: /workdir/mini.dec
The annotate step produces a GDX file mini_5b.gdx which can be used as input for the PIPS-IPM++ split tool (gmschk). It also automatically reads the number of blocks from mini.dec.
(pips-ipm++) > annotate
Executing: annotate /workdir/mini.mps --dec-file /workdir/mini.dec
Executing: /opt/venv/lib/python3.13/site-packages/gamspy_base/mps2gms /workdir/mini.mps DEC=/workdir/mini.dec GDX=mps2gms_with_dec.gdx GMS= PY=
readMPS: Trying to open file /workdir/mini.mps
readMPS: Read NAME OK
readMPS: Read OBJSENSE OK
readMPS: Read ROWS OK
readMPS: Read COLUMNS OK
readMPS: Read RHS OK
Number of PL entries in BOUNDS section is 20
readMPS: Read BOUNDS OK
Reading DEC file /workdir/mini.dec
Writing GDX mps2gms_with_dec.gdx
sh: 1: gdxcopy: not found
Warning: gdxClose returned with error 227, GDX file may not be readable.
Number of GDX i = 21
Number of GDX j = 20
Number of GDX a(i,j) = 56
Number of GDX c(j) = 20
Number of GDX q(j,j) = 0
Executing: /opt/venv/lib/python3.13/site-packages/gamspy_base/gams /mps2jac.gms --input_gdx=mps2gms_with_dec.gdx --output_gdx=/workdir/mini_5b o=/dev/null
--- Job mps2jac.gms Start 06/09/26 20:24:38 53.5.1 f2e5fac3 LEX-LEG x86 64bit/Linux
--- Applying:
/opt/venv/lib/python3.13/site-packages/gamspy_base/gmsprmun.txt
--- GAMS Parameters defined
Input /mps2jac.gms
Output /dev/null
ScrDir /workdir/225a/
SysDir /opt/venv/lib/python3.13/site-packages/gamspy_base/
AppendLog 1
--input_gdx mps2gms_with_dec.gdx
--output_gdx /workdir/mini_5b
Licensee: GAMS Demo, for EULA and demo limitations see G260130+0003Cc-GEN
https://www.gams.com/latest/docs/UG%5FLicense.html DC0000
/opt/venv/lib/python3.13/site-packages/gamspy_base/gamslice.txt
47c482f4-6e50-4644
v:2
Demo license for demonstration and instructional purposes only
System information: 12 physical cores and 32 Gb physical memory detected
GAMS 53.5.1 Copyright (C) 1987-2026 GAMS Development. All rights reserved
--- Starting compilation
--- mps2jac.gms(10) 2 Mb
--- $echo File /workdir/isEmpty.gms
--- mps2jac.gms(18) 2 Mb
--- GDXin=/workdir/mps2gms_with_dec.gdx
--- GDX File ($gdxIn) /workdir/mps2gms_with_dec.gdx
--- mps2jac.gms(22) 3 Mb
--- > isEmpty.gms(2) 3 Mb
--- mps2jac.gms(24) 3 Mb
--- > isEmpty.gms(2) 3 Mb
--- mps2jac.gms(25) 3 Mb
--- > isEmpty.gms(2) 3 Mb
--- mps2jac.gms(26) 3 Mb
--- > isEmpty.gms(2) 3 Mb
--- mps2jac.gms(27) 3 Mb
--- > isEmpty.gms(2) 3 Mb
--- mps2jac.gms(28) 3 Mb
--- > isEmpty.gms(2) 3 Mb
--- mps2jac.gms(29) 3 Mb
--- > isEmpty.gms(2) 3 Mb
--- mps2jac.gms(30) 3 Mb
--- > isEmpty.gms(2) 3 Mb
--- mps2jac.gms(71) 3 Mb
--- Starting execution: elapsed 0:00:00.003
--- mps2jac.gms(87) 4 Mb
--- GDX File (execute_unload) /workdir/mini_5b.gdx
*** Status: Normal completion
--- Job mps2jac.gms Stop 06/09/26 20:24:38 elapsed 0:00:00.004
Annotated GDX file: /workdir/mini_5b.gdx with 5 blocks
Annotation successful. Output: /workdir/mini_5b.gdx, Blocks: 5
The warning about gdxcopy: not found is harmless and can be ignored for the time being.
The split step splits mini_5b.gdx into one GDX file for each block (mini_5b0.gdx, ..., mini_5b5.gdx). It also creates a map file mini_5b.map that will be used to map the solution from PIPS-IPM++ back to the original variable and equations names and order.
(pips-ipm++) > split
Executing: split /workdir/mini_5b.gdx --blocks 5
Executing: /usr/local/bin/gmschk -T 6 /workdir/mini_5b.gdx
Reading equations stages
Reading variable stages
Copying i (#recs=22)
Copying j (#recs=21)
Copying jobj (#recs=1)
Copying jb (#recs=0)
Copying ji (#recs=0)
Copying iobj (#recs=1)
Copying objcoef (#recs=1)
Copying objconst (#recs=1)
Copying e (#recs=22)
Copying x (#recs=20)
Copying A (#recs=77)
Copying ANl (#recs=0)
cli solve --blocks 5 mini_5b
Split successful.
The solve step calls gmspips for the split-up GDX input files, using 5 MPI processes:
(pips-ipm++) > solve --mpi 5
Executing: solve /workdir/mini_5b --dec-file /workdir/mini.dec --blocks 5 --mpi 5
--- Executing Solve ---
Executing: mpirun --allow-run-as-root -n 5 /usr/local/bin/gmspips 6 /workdir/mini_5b
PIPS-IPM++ version 0.1.0 (githash: 8a9575ed)
A parallel interior-point solver for doubly bordered block diagonal linear programs (https://pips-ipmpp.gitlab.io)
reading /workdir/mini_5b
Using a total of 5 MPI processes.
[...]
PIPS-IPM++ completed
====================
Status: SUCCESSFUL_TERMINATION (Instance solved to optimality successfully)
Objective value: -10
Solving time: 0.0121345
Total time: 0.012139
Could not open GDX file for mini_5b_dict.gdx (errNr=2).
Not able to write the solution to GDX. Writing a .sol file.
Solution written to mini_5b.sol
Do read & annotate & split & solve in one call:
docker run -v $PWD/workdir:/workdir -w /workdir -it registry.gitlab.com/pips-ipmpp/pips-ipmpp/pips-ipmpp:latest cli solve mini.mps --dec-file mini.dec
Run on MPS file with automatic annotation
Retrieve an instance, for example SWITCH-hydrogen from Open Energy Benchmark:
curl -LO --output-dir workdir https://storage.googleapis.com/solver-benchmarks/instances/SWITCH-hydrogen-3-6ts.lp.gz
Start a container with the interactive shell:
docker run -v $PWD/workdir:/workdir -w /workdir -it registry.gitlab.com/pips-ipmpp/pips-ipmpp/pips-ipmpp:latest
Read the instance, annotate and split it, and then solve it:
read SWITCH-hydrogen-3-6ts.lp.gz
blocks 30
annotate
split
solve --mpi 8
quit
The annotation step now runs pipstools to find 30 blocks:
(pips-ipm++) > annotate
Executing: annotate /workdir/SWITCH-hydrogen-3-6ts.lp.gz --blocks 30
Executing: pipstools annotate /workdir/SWITCH-hydrogen-3-6ts.lp.gz --blocks 30
Running HiGHS 1.13.1 (git hash: 1d267d9): Copyright (c) 2026 under MIT licence terms
Number of PL entries in BOUNDS section is 657
*******************************************************************************
* Partitioning Context *
*******************************************************************************
Partitioning Parameters:
Hypergraph:
Mode: direct_kway
Objective: soed
Gain Policy: soed
Input File Format: hMetis
Instance Type: hypergraph
Preset Type: quality
Partition Type: multilevel_hypergraph_partitioning
k: 30
epsilon: 0.001
seed: 0
Number of V-Cycles: 0
Ignore HE Size Threshold: 1000
Large HE Size Threshold: 50000
...
Detected 14 linking variables
Detected 78 linking constraints (thereof 30 2-link constraints)
Scoring components (whitescore, link_var_global, link_equ_global, link_equ_twolink, est_root_dim, est_root_nnz)
[0.96866, 14, 48, 30, 106, 5088]
Total score: 96.86076
Writing gdx with gams.numpy API
Wrote file SWITCH-hydrogen-3-6ts_30b.gdx in 0.00 seconds
Annotated problem in 0.21 seconds
Annotated GDX file: SWITCH-hydrogen-3-6ts_30b.gdx with 30 blocks
Annotation successful. Output: /workdir/SWITCH-hydrogen-3-6ts_30b.gdx, Blocks: 30
The solve step runs with 8 MPI processes:
(pips-ipm++) > solve --mpi 8
Executing: solve /workdir/SWITCH-hydrogen-3-6ts_30b.gdx --blocks 30 --mpi 8
--- Executing Solve ---
Executing: mpirun --allow-run-as-root -n 8 /usr/local/bin/gmspips 31 /workdir/SWITCH-hydrogen-3-6ts_30b
PIPS-IPM++ version 0.1.0 (githash: 8a9575ed)
A parallel interior-point solver for doubly bordered block diagonal linear programs (https://pips-ipmpp.gitlab.io)
reading /workdir/SWITCH-hydrogen-3-6ts_30b.gdx
Using a total of 8 MPI processes.
...
PIPS-IPM++ completed
====================
Status: SUCCESSFUL_TERMINATION (Instance solved to optimality successfully)
Objective value: 1.88715e+08
Solving time: 0.101801
Total time: 0.101814
Could not open GDX file for SWITCH-hydrogen-3-6ts_30b_dict.gdx (errNr=2).
Not able to write the solution to GDX. Writing a .sol file.
Solution written to SWITCH-hydrogen-3-6ts_30b.sol
In one call:
docker run -v $PWD/workdir:/workdir -w /workdir -it registry.gitlab.com/pips-ipmpp/pips-ipmpp/pips-ipmpp:latest cli solve SWITCH-hydrogen-3-6ts_30b.lp.gz --blocks 30 --mpi 8
If a library libhsl.so with HSL linear solver MA57 is available in directory workdir, then make it available to PIPS-IPM++:
printf "LINEAR_LEAF_SOLVER ma57\nLINEAR_ROOT_SOLVER ma57\nLINEAR_SUB_ROOT_SOLVER ma57" > workdir/PIPSIPMpp.opt
docker run -v $PWD/workdir:/workdir -w /workdir -it registry.gitlab.com/pips-ipmpp/pips-ipmpp/pips-ipmpp:latest cli solve SWITCH-hydrogen-3-6ts_30b.lp.gz --blocks 30 --mpi 8 --hsl-lib /workdir
Run on GDX file with stage information
Retrieve some sample data (indus89.gdx):
mkdir workdir
curl -LO --output-dir workdir https://pips-ipmpp.gitlab.io/indus89.gdx
In this GDX file, a block decomposition is already stored via the stage attribute.
Start a container with the interactive shell:
docker run -v $PWD/workdir:/workdir -w /workdir -it registry.gitlab.com/pips-ipmpp/pips-ipmpp/pips-ipmpp:latest
(replace docker by podman for Podman; use apptainer run pips-ipmpp_latest.sif for Apptainer)
Read the sample, split it, and then solve it:
read indus89.gdx
blocks 9
split
solve --mpi 9
quit
Note that in case of reading an already annotated GDX file, we still need to provide the number of blocks. If that were not the case, then a call to annotate should be added.
(pips-ipm++) > read indus89.gdx
Successfully read input file: /workdir/indus89.gdx
(pips-ipm++) > blocks 9
(pips-ipm++) > split
Executing: split /workdir/indus89.gdx --blocks 9
Executing: /usr/local/bin/gmschk -T 10 /workdir/indus89.gdx
Reading equations stages
Reading variable stages
Copying i (#recs=2726)
Copying j (#recs=6570)
Copying jobj (#recs=1)
Copying jb (#recs=0)
Copying ji (#recs=0)
Copying iobj (#recs=1)
Copying objcoef (#recs=1)
Copying objconst (#recs=1)
Copying e (#recs=2726)
Copying x (#recs=6570)
Copying A (#recs=39489)
Copying ANl (#recs=0)
cli solve --blocks 9 indus89
Split successful.
(pips-ipm++) > solve --mpi 9
Executing: solve /workdir/indus89 --blocks 9 --mpi 9
--- Executing Solve ---
Executing: mpirun --allow-run-as-root -n 9 /usr/local/bin/gmspips 10 /workdir/indus89 writesol
PIPS-IPM++ version 0.1.0 (githash: 47fc8346)
A parallel interior-point solver for doubly bordered block diagonal linear programs (https://pips-ipmpp.gitlab.io)
reading /workdir/indus89
Using a total of 9 MPI processes.
Note: Problem converted to minimization sense for PIPS-IPM++. Objective values in solver log are negated!
...
PIPS-IPM++ completed
====================
Status: SUCCESSFUL_TERMINATION (Instance solved to optimality successfully)
Objective value: 114874 (with respect to original maximization sense)
Solving time: 2.96721
Total time: 2.96722
Could not open GDX file for /workdir/indus89_dict.gdx (errNr=2).
Not able to write the solution to GDX. Writing a .sol file.
Solution written to /workdir/indus89.sol