Initializing help system before first use

Introduction

The xpress package provides an R interface to the FICO Xpress optimizer. The interface consists of two parts:

  • a thin wrapper for the C library. All the solver functionality is available through this wrapper.
  • a set of convenience function that allow a more “R-like” experience when interacting with the solver.

Thin Wrapper API

The lowest level of the optimizer’s R interface is a thin wrapper for the C library. People familiar with the C API of the solver will find that things look pretty similar to C. There are a few differences, though:

  • In case of error, functions do not return non-zero status codes. Instead they raise an error by calling stop() with an appropriate error message.
  • Functions that have more than one output do not return these outputs via output parameters but instead return a list that contains all the output arguments.
  • Functions that don’t have an explicit return value return the problem object. This way you can easily chain function calls, for example by using the %>% operator from the magrittr package.

Note that the C library does not operate with vectors and matrices like R does. Instead it works with arrays of indices and non-zero values. Indexing in these arrays is 0-based, so that the first variable, constraint, … will have index 0. This is in contrast to R matrices and vectors for which indexing is 1-based. This must be kept in mind and taken care of when passing data to the optimizer and reading back results.

The convenience functions listed in the next section allow to directly exchange R matrices and vectors with the solver.

Creation and Deletion of Problems

In order to interact with the solver, a problem object is required. These problems are created by means of the createprob() function:

prob <- createprob()

Note that createprob() implicity calls init("") in order to initialize licensing if that did not happen yet.

Attached to the problem object are native resources, i.e., resources that are not managed by R. It is thus the user’s responsibility to free the problem object when it is no longer needed. The object is freed using function destroyprob(). Good ways to make sure the object is deleted is to use on.exit() or tryCatch:

prob <- createprob()
# make sure problem is deleted when function exits
on.exit(destroyprob(prob))
# or alternatively, execute code in a tryCatch:
tryCatch({ ... },
         finally = { destroyprob(prob) })

Callbacks

The low-level API supports callback. There is one caveat, though: Since R is inherently single-threaded and cannot be called back into from other threads than the main thread, all callback invocations have to be routed through the main thread. The Xpress optimizer supports this by setting the XPRS_CALLBACKFROMMASTERTHREAD control to 1. This may result in some performance degradation, though.

A callback in the Xpress R interface is just a function or closure with the correct number of arguments. Please refer to the (C) reference documentation to learn what callbacks exist, how many arguments the functions require and what the callbacks can do.

A callback may return one of the following:

  • NULL or NA. These values are just ignored.
  • A length-one integer vector. If the corresponding C callback allows returning a value (usually in order to stop the search or indicate some other action) then this value is passed back to the optimizer, otherwise is is ignored.
  • A list. If the corresponding C callback allows returning values then those values are read from the list and returned to the optimizer. The list may also contain a field “ret”, which must be an integer and is treated the same as the single integer return value described above.

A warning is issued in case a callback returns something else or returns a list with unknown elements.

Convenience Functions

The FICO Xpress optimizer interface provides a set of convenience functions that are based on the thin wrapper described above but allow directly using R data structures like vectors and matrices directly.

Please refer to the documentation for functions xprs_loadproblemdata and xprs_optimize for further details. These functions allow using R matrices and vectors directly in order to solve optimization problems.

Installation

Please refer to the INSTALL.txt in the R directory of your Xpress installation for installation instructions.

Licensing

To run the Optimizer from the R interface it is necessary to have a valid licence file, “xpauth.xpr”. Please see the license chapter of the FICO Xpress Online Documentation for more information on licensing options.

The FICO Xpress licensing system is highly flexible and is easily configurable to cater for the user’s requirements. The system can allow the Optimizer to be run on a specific machine, on a machine with a specific ethernet address or on a machine connected to an authorized hardware dongle.

On Unix systems it is necessary to set the XPAUTH_PATH environment variable to the full path to the license file. For ease of support it is recommended that the license file is placed in the bin directory within your Xpress installation and the XPAUTH_PATH environment variable is set accordingly before launching an R session , e.g.,

export XPAUTH_PATH=/opt/xpressmp/bin/xpauth.xpr
R

An alternative is to provide the path by calling the init function with the path to the license inside R:

library(xpress)
init("/opt/xpressmp/bin/xpauth.xpr")

Another alternative is to place the file “xpauth.xpr” into your current working directory before starting your R session, from where it will be picked up automatically.

On Windows operating systems the Optimizer searches for the license file in the directory containing the Xpress libraries, which are installed by default into the “C:\xpressmp\bin” folder. To avoid unnecessary licensing problems, it is recommended that the XPAUTH_PATH environment variable is not set on Windows.

A First LP Problem

For starters, we solve the following 2-variable model from R using the Xpress-R interface.

\begin{aligned}[t] & \min & x_1 + x_2 \\ & & 5 x_1 + x_2 & \geq 7 \\ & & x_1 + 4 x_2 & \geq 9 \\ & & x_1, x_2 & \geq 0 \end{aligned}

suppressMessages(library(xpress))

# create a list object to hold all input
problemdata <- list()

# objective coefficients
problemdata$objcoef <- c(1, 1)

# row coefficients as a single matrix object
problemdata$A <- matrix(c(5, 1, 1, 4), nrow = 2, byrow = TRUE)

# right-hand side
problemdata$rhs <- c(7, 9)

# row sense
problemdata$rowtype <- c("G", "G")

# lower bounds (automatically 0 if not present)
problemdata$lb <- c(0, 0)

# upper bounds(automatically inferred if not present)
problemdata$ub <- c(Inf, Inf)

# names for writing to MPS/LP files
problemdata$colname <- c("x_1", "x_2")

# Problem Name displayed when the solver solves the problem.
problemdata$probname <- "FirstExample"

We now load everything into a new XPRSprob ‘p’. xprs_loadproblemdata can be used to load all problem data at once into an existing or new problem object. In this case, we have no existing XPRSprob object. xprs_loadproblemdata creates one for us. For convenience and the use inside pipes, xprs_loadproblemdata returns the prob pointer.

prob <- xprs_loadproblemdata(problemdata = problemdata)
# You may also use the equivalent
# prob <- createprob()
# xprs_loadproblemdata(prob, problemdata = problemdata)

The newly created XPRSprob object supports the print and summary statements. Let’s get an overview of the optimization problem loaded into prob

print(prob)
## XPRESS problem object FirstExample
##      2 rows         2 cols        4 elems
##      0 entities      0 sets        0 indicators
##      0 qelems        0 qcelems
##      0 gencons       0 pwlcons

We use xprs_optimize to solve the model. This again returns ‘prob’, which can be summarized using the base function summary

summary(xprs_optimize(prob))
##
## Objective value                     : 3.00000e+00
## Max primal violation      (abs/rel) : 0 / 0
## Max dual violation        (abs/rel) : 0 / 0
## LPSTATUS: 1

It may be useful for further processing to convert the solution into a data frame.

print(data.frame(Variable = problemdata$colname, Value = getsolution(prob)$x))
##   Variable Value
## 1      x_1     1
## 2      x_2     2

© 2001-2024 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.