Introduction

The purpose of this manual is to make user familiar with the package localsolver, which allows for using the LocalSolver device to solve optimization problems by using GNU R environment.

Solving a 'LocalSolver' problem with R

Creating an ls.problem object

The first step to solve a 'LocalSolver' problem is to create an object of class ls.problem. This can be done by the function ls.problem, which returns an object of such class.

This function requires at least one argument - model.text.lsp - a string with model formulation in the special programming language, conected directly with the LocalSolver software. For more details, please visit the web site: http://www.localsolver.com/.

The model description in the LocalSolver language should include the declaration of decision variables, the formulation of the constraints (in terms of model parameters) and the objective funcions to be minimized or maximized. All the objective functions or constraints whose values we would like to extract from the problem must be named.

As an example, let us formulate and solve a knapsack problem. The task consists in filling a knapsack with some of four available items. Each item has its value and its weight. The overall weight of the items in the knapsack should not exceed a weight bound, called knapsackBound. The objective is to maximize the sum of values of the items in the knapsack, without exceeding the knapsackBound value with their overall weight.

model <- "function model() {
  x[i in 1..4] <- bool();

  // weight constraint
  knapsackWeight <- sum[i in 1..nbItems](itemWeights[i] * x[i]);
  constraint knapsackWeight <= knapsackBound;

  // maximize value
  knapsackValue <- sum[i in 1..nbItems](itemValues[i] * x[i]);
  maximize knapsackValue;
}"
lsp <- ls.problem(model)
lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model() {
##   x[i in 1..4] <- bool();
## 
##   // weight constraint  
##   knapsackWeight <- sum[i in 1..nbItems](itemWeights[i] * x[i]);
##   constraint knapsackWeight <= knapsackBound;
##   
##   // maximize value
##   knapsackValue <- sum[i in 1..nbItems](itemValues[i] * x[i]);
##   maximize knapsackValue;
## }
## 
## *** No output expressions have been chosen. ***
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  indexFromZero: FALSE

To create an object of class ls.problem it is necessary that the LocalSolver program is installed on the computer. LocalSolver on installation adds its application folder to system PATH environment variable so post installation is usually available in environment. If it is not (e.g. if installed after R environment has been loaded) then it's necessary to provide path to LocalSolver executable under ls.path argument to ls.problem function. It is important to pass the correct file path, as LocalSolver must be available (and its availability is checked) for the function to create an object of class ls.problem.

Changing the settings of a ls.problem object

The object of class ls.problem consists of some components, which allow for building the problem gradually, until all the model settings are satisfactory for the user. Each component can be changed by a corresponding function.

Note that all the functions change an object of class ls.problem, returning a new one, so in order to change the problem, the user must assign the result of each function to it. For example, the command:

set.params(lsp, lsTimeLimit=60)
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model() {
##   x[i in 1..4] <- bool();
## 
##   // weight constraint  
##   knapsackWeight <- sum[i in 1..nbItems](itemWeights[i] * x[i]);
##   constraint knapsackWeight <= knapsackBound;
##   
##   // maximize value
##   knapsackValue <- sum[i in 1..nbItems](itemValues[i] * x[i]);
##   maximize knapsackValue;
## }
## 
## *** No output expressions have been chosen. ***
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  lsTimeLimit: 60
##    *  indexFromZero: FALSE

will not change tne lsp object, whereas

lsp <- set.params(lsp, lsTimeLimit=60)

will set the time limit of the object lsp to 60 seconds.

Setting solver parameters

The first thing which needs to be set before solving the problem is to add some solver parameter settings. This can be done by the set.params function, whose parameters correspond to the following available solver parameters:

If the function is called many times on the same object, the settings are updated (the values parameters chosen, when the function is called once again, will be changed, and the parameters whose values were set before, will not be changed if they ar not called once again).

For example:

lsp <- set.params(lsp, lsTimeLimit=60, lsIterationLimit=250)
lsp <- set.params(lsp, lsTimeLimit=300, lsSeed=7)
lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model() {
##   x[i in 1..4] <- bool();
## 
##   // weight constraint  
##   knapsackWeight <- sum[i in 1..nbItems](itemWeights[i] * x[i]);
##   constraint knapsackWeight <= knapsackBound;
##   
##   // maximize value
##   knapsackValue <- sum[i in 1..nbItems](itemValues[i] * x[i]);
##   maximize knapsackValue;
## }
## 
## *** No output expressions have been chosen. ***
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  lsTimeLimit: 300
##    *  lsIterationLimit: 250
##    *  lsSeed: 7
##    *  indexFromZero: FALSE

In this case, the value of the parameter lsTimeLimit has been substituted by a new one, and the argument lsIterationLimit has not changed.

It is necessary that either time or iteration number limit is provided to solve the problem. The other parameters are not obligatory.

It is also possible to reset the parameters fastly:

lsp <- reset.lsp.params(lsp)
lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model() {
##   x[i in 1..4] <- bool();
## 
##   // weight constraint  
##   knapsackWeight <- sum[i in 1..nbItems](itemWeights[i] * x[i]);
##   constraint knapsackWeight <= knapsackBound;
##   
##   // maximize value
##   knapsackValue <- sum[i in 1..nbItems](itemValues[i] * x[i]);
##   maximize knapsackValue;
## }
## 
## *** No output expressions have been chosen. ***
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  indexFromZero: FALSE

Adding output expressions

Solving an optimization problem consists in maximizing or minimizing the value of some objective function or functions. However, the user may also be interested in values taken by some constraint equations or decision variables. The localsolver allows for choosing those of these objects (which can be commonly referred to as output expressions), whose values the user wants to know. Each output expression, whose value we need to extract, must be added to the problem by the function add.output.expr. The arguments which need to be passed to the function are the object name and dimension (or 2-or-3-dimensional vector of dimensions, in case of a matrix or an array). The default value of the object dimension is 1, so it does not have to be provided in case of parameters which are single numbers. It is important that the object names are consistent with those used in the model formulation in the LocalSolver language.

lsp <- add.output.expr(lsp, "x", 4)
lsp <- add.output.expr(lsp, "knapsackWeight")
lsp <- add.output.expr(lsp, "knapsackValue")
lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model() {
##   x[i in 1..4] <- bool();
## 
##   // weight constraint  
##   knapsackWeight <- sum[i in 1..nbItems](itemWeights[i] * x[i]);
##   constraint knapsackWeight <= knapsackBound;
##   
##   // maximize value
##   knapsackValue <- sum[i in 1..nbItems](itemValues[i] * x[i]);
##   maximize knapsackValue;
## }
## 
## *** Required output expressions: ***
##    *  x - object of dimension 4
##    *  knapsackWeight - object of dimension 1
##    *  knapsackValue - object of dimension 1
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  indexFromZero: FALSE

As in case of solver parameters, it is possible to clear all the output expressions by calling the clear.output.exprs function:

lsp <- clear.output.exprs(lsp)
lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model() {
##   x[i in 1..4] <- bool();
## 
##   // weight constraint  
##   knapsackWeight <- sum[i in 1..nbItems](itemWeights[i] * x[i]);
##   constraint knapsackWeight <= knapsackBound;
##   
##   // maximize value
##   knapsackValue <- sum[i in 1..nbItems](itemValues[i] * x[i]);
##   maximize knapsackValue;
## }
## 
## *** No output expressions have been chosen. ***
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  indexFromZero: FALSE

Solving the problem

Once we have defined the LocalSolver problem and set its parameters, we can solve it. The only thing we need to provide is an input data list.

Input data

The input data for a localsolver problem should be provided as a named list of the parameters - coefficients of the problem constraints, which are constant. Each parameter must be of one of the following formats:

newElement <- list()
newElement[[1]] <- c(1,2,3.14)
newElement[[4]] <- c(1.6,2.77, 5, 34, 1)
newElement[[6]] <- 3
newElement
## [[1]]
## [1] 1.00 2.00 3.14
## 
## [[2]]
## NULL
## 
## [[3]]
## NULL
## 
## [[4]]
## [1]  1.60  2.77  5.00 34.00  1.00
## 
## [[5]]
## NULL
## 
## [[6]]
## [1] 3

In this case, the user can refer in the model to the indexes: [1,1], [1,2], [1,3], [4,1], ..., [4,5] and [6,1] of the array newElement (or [0,0], [0,1], [0,2], [3,0], ..., [3,4] and [5,0], if indexFromZero = TRUE.)

The elements of the input data list need to be indexed by their names, which should correspond to the parameter names used in the model description.

It is also important that all the data types are consistent with those declared in the model description. A matrix, array or vector of elements of class numeric in R will be treated as double by LocalSolver. If the parameters are of class integer in R, they will be passed as integers to LocalSolver.

All the parameters, to which the user refers in the model description, should be provided in the input data, and all the elements of the input data list should be used in the model.

For the knapsack problem, the exemplary input data may be the following:

data <- list(nbItems=4L, itemWeights=c(1L,2L,3L,4L), 

             itemValues=c(5,6,7,8), knapsackBound = 9L)

Solving the problem

To solve an optimization problem with localsolver, the function ls.solve needs to be called with two arguments:

The function returns the list of the output expressions, which have been chosen by the add.output.expr function (matrices, vectors or numbers).

Let us see how the function works in case of the knapsack example:

lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model() {
##   x[i in 1..4] <- bool();
## 
##   // weight constraint  
##   knapsackWeight <- sum[i in 1..nbItems](itemWeights[i] * x[i]);
##   constraint knapsackWeight <= knapsackBound;
##   
##   // maximize value
##   knapsackValue <- sum[i in 1..nbItems](itemValues[i] * x[i]);
##   maximize knapsackValue;
## }
## 
## *** Required output expressions: ***
##    *  x - object of dimension 4
##    *  knapsackWeight - object of dimension 1
##    *  knapsackValue - object of dimension 1
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  lsTimeLimit: 60
##    *  lsIterationLimit: 250
##    *  indexFromZero: FALSE
data
## $nbItems
## [1] 4
## 
## $itemWeights
## [1] 1 2 3 4
## 
## $itemValues
## [1] 5 6 7 8
## 
## $knapsackBound
## [1] 9
ls.solve(lsp, data)
## $x
## [1] 0 1 1 1
## 
## $knapsackWeight
## [1] 9
## 
## $knapsackValue
## [1] 21

The function has returned the list of values of the output expressions we asked for: the decision vector x, the value of the constraint knapsackWeight and of the objective function knapsackValue. Those values correspond to the optimal solution found by the solver.

More examples

The production problem

This problem consists in organizing production of 4 kinds of uncountable products. Each product requires certain production time, certain amount of raw and pre-processed materials, and its unit can be sold at a determined price. There are determined constraints for the overall materials and production times of all the products. The objective is to maximize the revenue of the produced items.

  model <- "function model() {
  x[i in 1..4] <- float(0,100);

  // time constraint
  productionTime <- sum[i in 1..4](time[i] * x[i]);
  constraint productionTime <= 200;

  // raw material constraint
  rawMaterials <- sum[i in 1..4](materialsR[i] * x[i]);
  constraint rawMaterials <= 300;

  // pre produced material constraint
  preMaterials <- sum[i in 1..4](materialsP[i] * x[i]);
  constraint  preMaterials <= 500;

  //maximize revenue
  revenue <- sum[i in 1..4](price[i] * x[i]);
  maximize revenue;

}

lsp <- ls.problem(model)
lsp <- set.params(lsp, lsTimeLimit=60, lsIterationLimit=250)
lsp <- add.output.expr(lsp, "x", 4)
lsp <- add.output.expr(lsp, "revenue")
lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model() {
##   x[i in 1..4] <- float(0,100);
## 
##   // time constraint
##   productionTime <- sum[i in 1..4](time[i] * x[i]);
##   constraint productionTime <= 200;
##   
##   // raw material constraint
##   rawMaterials <- sum[i in 1..4](materialsR[i] * x[i]);
##   constraint rawMaterials <= 300;
## 
##   // pre produced material constraint
##   preMaterials <- sum[i in 1..4](materialsP[i] * x[i]);
##   constraint  preMaterials <= 500;
## 
##   //maximize revenue
##   revenue <- sum[i in 1..4](price[i] * x[i]);
##   maximize revenue;
## 
## }
## 
## *** Required output expressions: ***
##    *  x - object of dimension 4
##    *  revenue - object of dimension 1
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  lsTimeLimit: 60
##    *  lsIterationLimit: 250
##    *  indexFromZero: FALSE
data <- list(time=c(5L,2L,6L,3L), materialsR=c(10L,8L,9L,7L), 
             materialsP=c(10L,20L,25L,22L), price=c(5L,4L,6L,3L) )
ls.solve(lsp, data)
## $x
## [1] 18.75  0.00 12.50  0.00
## 
## $revenue
## [1] 168.8

Mor examples

The examples above have been prepared as demos for the package users. After installing the package, the full list of the demos and can be viewed by the command:

demo(package = 'localsolver')

The particular demos may be activated by calling the same command and specyfying the demo name, for instance:

demo(assignment, package='localsolver')

Note that some of the examples have more than 1000 decision variables and therefore demand the full license for the 'LocalSolver' program.

Other features

Functions in LocalSolver language

The model formulation in the LocalSolver language, passed to the ls.problem object requires at least the model() part. However, the user is free to introduce other functions, too. In particular, it is possible to declare the param() function in the LocalSolver language (the package does not support all the possibilities connected with LocalSolver initial settings, such as the initial values of the decision variables). This can be followed by setting some particular solver parameters by the set.params function. In this case, the latter function will be called as the second one, which means that if some parameters appear in both parts, their settings in the problem will correspond to the values chosen by the set.params function. On the other hand, if some parameters are set by the function param() and the are not changed by the set.params function, their values will correspond to the ones provided in the param() part of the model formulation in the LocalSolver language.

For example:

model <-
  "function model(){
    x[0..nbProcesses-1][0..nbMachines-1] <- bool();
    //some model formulation
  }

  function param(){
    for [p in 0..nbProcesses-1][m in 0..nbMachines-1]
      if (m == initialMachine[p]) setValue(x[p][m], true);
      else setValue(x[p][m], false);
    ltTimeLimit = 60;
  }"  
lsp <- ls.problem(model)
lsp <- set.params(lsp, lsTimeLimit=300)
lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model(){
##     x[0..nbProcesses-1][0..nbMachines-1] <- bool();
##     //some model formulation
##   }
##   
##   function param(){
##     for [p in 0..nbProcesses-1][m in 0..nbMachines-1]
##       if (m == initialMachine[p]) setValue(x[p][m], true);
##       else setValue(x[p][m], false);
##     ltTimeLimit = 60;
##   }
## 
## *** No output expressions have been chosen. ***
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  lsTimeLimit: 300
##    *  indexFromZero: FALSE

The time limit will be overwritten by the value passed to the model by the set.params function during the process of solving the problem.

Changing the working directory for the package

The process of solving an optimization problem in the localsolver package requires generating some auxiliary files which are then passed to the LocalSolver program. In some special cases, for example, if the user needs to call the process several times in parallel, the working directory for these auxiliary files must be changed. This can be done by the set.temp.dir function, for example:

lsp <- ls.problem("function model(){ ... }")
lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model(){ ... }
## 
## *** No output expressions have been chosen. ***
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky
## 
## *** Solver parameters: ***
##    *  indexFromZero: FALSE
lsp <- set.temp.dir(lsp, path=file.path(tempdir(), '..'))
lsp
## *** A LocalSolver optimization problem ***
## 
## *** Model formulation: ***
## function model(){ ... }
## 
## *** No output expressions have been chosen. ***
## 
## *** Temporary directory: ***
## C:\Users\ws171913\AppData\Local\Temp\RtmpsLM8Ky/..
## 
## *** Solver parameters: ***
##    *  indexFromZero: FALSE

The temporary directory has been changed to the path provided to the function.