---
title: "Solving MathProg, LP and MPS models with glpsol"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Solving MathProg, LP and MPS models with glpsol}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

```{r setup}
library(glpsol)
```

## Overview

`glpsol` is the stand-alone solver of the GNU Linear Programming Kit (GLPK).
It reads a model, solves it and writes the solution to a file. It is the
reference implementation of **MathProg** (GMPL), the modelling language of
GLPK, and it also reads **CPLEX LP** and **MPS** files.

The solver is normally obtained by installing GLPK. This package builds and
installs the executable as part of the R package, so that

```r
install.packages("glpsol")
```

is sufficient on Windows and macOS, where the CRAN binary contains its own copy
of GLPK. On Linux the package is installed from source and requires the GLPK
development files (`libglpk-dev` or `glpk-devel`); see the README. In either
case the executable does not need to be on the `PATH`:

```{r where}
glpsol_path()
glpk_version()
```

`glpk_version()` returns the version of the GLPK library against which the
package was built.

## Solving a MathProg model

`glpsol_run()` accepts the arguments of the `glpsol` command line. The package
includes the transportation example distributed with GLPK:

```{r solve}
mod <- system.file("models", "transp.mod", package = "glpsol")

out <- tempfile("glpsol-")
dir.create(out)
sol <- file.path(out, "transp.sol")

res <- glpsol_run(c("-m", mod, "-o", sol), echo = FALSE)
res$status
```

`status` is the exit status of the process: `0` indicates that `glpsol` ran to
completion. It does not indicate whether the model was feasible; that
information is in the log:

```{r log}
tail(res$log, 4)
```

The solution is written to the file given with `-o`. Its `Status` line states
the outcome:

```{r readsol}
writeLines(head(readLines(sol), 12))
grep("^Status", readLines(sol), value = TRUE)
```

This printable format is intended for inspection rather than parsing. To load
the solution into R, request the plain-text format with `-w` and the problem
file with `--wglp` in the same run. The first contains the values by index, the
second the row and column names:

```{r readback}
txt <- file.path(out, "transp.txt")
glp <- file.path(out, "transp.glp")
glpsol_run(c("-m", mod, "-w", txt, "--wglp", glp), echo = FALSE)

s <- glpsol_read_solution(txt, names = glp)
s
s$cols[, c("name", "status", "primal", "dual")]
s$rows[, c("name", "primal", "dual")]
```

`glpsol_read_solution()` reads the three kinds of solution that `glpsol` can
write (basic, interior-point and integer) and reports the kind in `kind`. A
model can also produce its own output with the MathProg `printf` and `table`
statements. The most commonly used options are:

| Option | Effect |
|---|---|
| `-o file` | write the solution in printable format |
| `-w file` | write the solution in plain-text format |
| `-y file` | write MathProg `printf`/`display` output to a file |
| `--wlp file`, `--wfreemps file` | write the generated problem in CPLEX LP or free MPS format |
| `--check` | translate and generate the model without solving it |
| `--tmlim n` | limit the search to `n` seconds |

`glpsol_run("--help")` lists the complete set.

## Separate data files

A MathProg model and its data are frequently kept in separate files; the `-d`
option names the data file. The example above contains its own `data;` section,
so a single file suffices. With a separate data file the call is

```r
glpsol_run(c("-m", "model.mod", "-d", "model.dat", "-o", "model.sol"))
```

## LP and MPS files

The same executable reads the two common exchange formats, so a problem written
by another tool can be solved in the same way:

```r
glpsol_run(c("--lp",      "model.lp",  "-o", "model.sol"))   # CPLEX LP
glpsol_run(c("--freemps", "model.mps", "-o", "model.sol"))   # free MPS
glpsol_run(c("--mps",     "model.mps", "-o", "model.sol"))   # fixed MPS
```

The LP method is selected with `--simplex` (the default) or `--interior`. A
model with integer variables is solved by branch-and-cut; `--nomip` relaxes the
integrality constraints and solves the LP instead.

## Monitoring and limiting a run

With `echo = TRUE` the log is written to the console as it is produced, which
is useful for large models that run for a long time:

```r
glpsol_run(c("-m", "big.mod"), echo = TRUE)
```

In this mode the output is not captured and `log` is empty; use `echo = FALSE`,
as in the examples above, to retain it.

`timeout` terminates the process after the given number of seconds:

```{r timeout}
res <- glpsol_run(c("-m", mod), echo = FALSE, timeout = 60)
res$timed_out
```

This differs from the `--tmlim` option of GLPK, which limits the search phase
only. `timeout` applies to the whole run, including model translation and
output, and is available on all platforms. A run terminated in this way returns
`timed_out = TRUE` and `status = 124` instead of raising an error.

## Use from other packages

`glpsol_path()` is the intended integration point. A package that generates
MathProg can pass the path to `system2()` and keep its own calling convention.
The dependency can be made optional as follows:

```r
find_glpsol <- function() {
  if (requireNamespace("glpsol", quietly = TRUE)) {
    hit <- tryCatch(glpsol::glpsol_path(), error = function(e) NULL)
    if (!is.null(hit)) return(hit)
  }
  Sys.which("glpsol")            # fall back to an installed copy
}
```

This is the lookup used by [multimod](https://github.com/optimal2050/multimod)
and [energyRt](https://github.com/optimal2050/energyRt): the bundled
executable is consulted after any explicitly configured path and before copies
found on the `PATH`, because its version is known.

## Relation to other packages

[glpkAPI](https://CRAN.R-project.org/package=glpkAPI), the R binding to the
GLPK C API, was archived from CRAN in March 2026. This package fills the
resulting gap at the command-line level: `install.packages()` provides the
solver, `glpsol_run()` executes it in a separate process, and
`glpsol_read_solution()` reads the result. It does not provide an interface to
the C API.

Two alternatives remain for that purpose.
[Rglpk](https://CRAN.R-project.org/package=Rglpk) is on CRAN and solves a
matrix-level LP in-process with `Rglpk_solve_LP()`; it reads a single MathProg
file but not separate data files. glpkAPI itself can still be installed from
the [CRAN archive](https://CRAN.R-project.org/src/contrib/Archive/glpkAPI/)
against a system GLPK installation and provides the full API, including named
rows and columns, sensitivity analysis and callbacks.

```{r cleanup, include = FALSE}
unlink(out, recursive = TRUE)
```
