PIPS-IPM++ Solver and Tools
a parallel interior-point method for doubly bordered block diagonal linear programs
The PIPS-IPM++ Docker Image

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.

  1. 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.
  2. Options Files: If specific solver options are required, place a file named PIPSIPMpp.opt in this directory.
  3. 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.gdxblocks 10annotate
Split a GDX with 10 blocks read prob.gdxblocks 10split
Solve a GDX with 10 blocks read prob.gdxblocks 10solve
Solve from a filestem (10 blocks) read prob_stemsolve --blocks 10
Annotate an MPS with 5 blocks read model.mpsannotate --blocks 5 (Result: model_5b.gdx)
Annotate using a DEC file read model.mps model.decannotate (Result: annotated GDX file)
Print the final solution read prob.gdxblocks 10solvesolution
Print the values of the final solution read prob.gdxblocks 10solvesolution 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