Main mode

In the main mode, the modeler behaves like an ordinary programming language. This mode is opposed to the default mode (called classic mode) in which you have to fill in a mandatory model() method completed by optional methods input(), param(), display() and output().

In the main mode, you are free to do what you want, without being constrained by the formalism of the classic mode: you can load or save models at any time, run several successive optimizations or even use LSP for its scripting language capabilities without running any optimization at all. In return, it is up to you to close the models, launch the resolutions and manage the lifetime of the solvers that you instantiate.

Enabling the main mode

To use the main mode, simply declare a main method in your LSP program. This method replace the methods input(), model(), param(), display() and output() of the classic mode:

function main() {
    // Anything you want
}

Launching an LSP file with the main mode is done in the same way as for the classic mode. Use the executable localsolver followed by the name of the lsp file:

localsolver fileMainMode.lsp

Modeling and optimization in the main mode

In the main mode, you have to create, close and launch the optimization model explicitely. There is no implicit localsolver object. You must create one and manage its lifetime before you can start modeling your problem. For that, use the module method localsolver.create() associated to the with syntax:

use localsolver;

function main() {
    with(ls = localsolver.create()) {
        x <- bool();
        ...
        maximize obj;
        ...
        ls.model.close();
        ls.param.timeLimit = 2;
        ls.solve();
    }
}

The use of the with syntax is mandatory to allow the virtual machine to know which LocalSolver instance is active when using the minimize, maximize, constraint keywords and the <- operator.

In addition, all the global variables prefixed with ls such as lsTimeLimit, lsVerbosity, lsSolution or lsStatistics are disabled: they no longer have specific features associated to them. They can nevertheless be used as ordinary variables.

All these features must be replaced by specific calls to functions, types or attributes exposed in the LocalSolver module.

Passing arguments

In the classic mode, you can modify and create global variables in an LSP program directly from the command line. This mechanism does not exist in the main mode. However, you can still pass arguments from the command line to your program. The mechanism to use is very similar to other programming languages. Simply add an args argument to your main function. This variable is optional and is conventionally called args, but any name works. It will contain each argument passed to the command line as a map indexed by integers starting at 0. The arguments will always be strings, no transformation is applied to the argument values. Also note that the name of the localsolver executable and the name of the LSP script are not part of the argument list.

Example:

function main(args) {
    println("Nb args: ", args.count());
    println("All arguments: ");
    for[index,arg in args] {
        println("- Argument #", index, ": ", arg);
    }
}

This program executed with the following command line:

localsolver file.lsp arg0 "arg1 with multiple words" 12

Will display:

Nb args: 3
All arguments:
- Argument #0: arg0
- Argument #1: arg1 with multiple words
- Argument #2: 12

Note

The delimiter used to separate arguments on the command line depends entirely on the terminal you are using. It is usually a space. If you need to pass an argument that contains more than one word, you usually need to group them around double quotes or escape the spaces with a backslash.

A complete example

The following code is a port of the knapsack example into the main mode. The code of the classic mode is also put in the next tab for comparison.

use localsolver;
use io;

function main(args) {
    if(args.count() == 0 || args.count() > 3) {
        local usage = "Usage: localsolver knapsackMain.lsp inputFile [outputFile [timeLimit]]";
        throw usage;
    }

    local inputFile = args[0];
    local outputFile = args[1];
    local timeLimit = args[2] != nil ? args[2].toInt() : 20;

    with(ls = localsolver.create()) {
        parseInput(inputFile);

        x[i in 0...nbItems] <- bool();
        knapsackWeight <- sum[i in 0...nbItems](weights[i] * x[i]);
        constraint knapsackWeight <= knapsackBound;
        knapsackValue <- sum[i in 0...nbItems](prices[i] * x[i]);
        maximize knapsackValue;

        ls.model.close();
        ls.param.timeLimit = timeLimit;
        ls.solve();

        if(outputFile != nil)
            saveSolution(outputFile);
    }
}

function parseInput(fileName) {
    local inFile = io.openRead(fileName);
    nbItems = inFile.readInt();
    weights[i in 0...nbItems] = inFile.readInt();
    prices[i in 0...nbItems] = inFile.readInt();
    knapsackBound = inFile.readInt();
}

function saveSolution(fileName) {
    local solFile = io.openWrite(fileName);
    solFile.println(knapsackValue.value);
    for [i in 0...nbItems : x[i].value == 1]
        solFile.print(i + " ");
    solFile.println();
}
use io;

/* Read instance data */
function input() {
    local usage = "Usage: localsolver knapsack.lsp "
            + "inFileName=inputFile [solFileName=outputFile] [lsTimeLimit=timeLimit]";

    if (inFileName == nil) throw usage;

    local inFile = io.openRead(inFileName);
    nbItems = inFile.readInt();
    weights[i in 0...nbItems] = inFile.readInt();
    prices[i in 0...nbItems] = inFile.readInt();
    knapsackBound = inFile.readInt();
}

/* Declare the optimization model */
function model() {
    // Decision variables x[i]
    x[i in 0...nbItems] <- bool();

    // Weight constraint
    knapsackWeight <- sum[i in 0...nbItems](weights[i] * x[i]);
    constraint knapsackWeight <= knapsackBound;

    // Maximize value
    knapsackValue <- sum[i in 0...nbItems](prices[i] * x[i]);
    maximize knapsackValue;
}

/* Parametrize the solver */
function param() {
    if (lsTimeLimit == nil) lsTimeLimit = 20; 
}

/* Write the solution in a file */
function output() {
    if (solFileName == nil) return;
    local solFile = io.openWrite(solFileName);
    solFile.println(knapsackValue.value);
    for [i in 0...nbItems : x[i].value == 1]
        solFile.print(i + " ");
    solFile.println();
}