Two New Big Features for LSP Lovers
With the release of LocalSolver 10.0, one of the key features we wanted to push forward was integrating LSP programs into full production-ready applications. The JSON module and the Modeler API were both introduced to facilitate data processing in and out of an LSP program. In this article, we present these features with small examples. We recommend looking at the language reference if you are not familiar with LSP.
Let’s take the Knapsack LSP program from the example tour and walk through how these features can help us.
// LSP model for the Knapsack Problem
function model() {
// 0-1 decisions
x[i in 0..nbItems-1] <- bool();
// weight constraint
knapsackWeight <- sum[i in 0..nbItems-1](weights[i] * x[i]);
constraint knapsackWeight <= knapsackBound;
// maximize value
knapsackValue <- sum[i in 0..nbItems-1](prices[i] * x[i]);
maximize knapsackValue;
}
JSON module
The newly added JSON module allows the parsing of JSON data into LSP values. It can be handy to read the input data for a problem and export its solution afterward. Looking at our program, we can see that the model
function requires some variables to be initialized before it can be executed. Specifically, the variables nbItems
, weights
, prices
, and knapsackBound
need to be set. We could take JSON data file of the form:
{
"nbItems": 5,
"weights": [5, 3, 2, 1, 3],
"prices": [10, 9, 5, 6, 3],
"knapsackBound": 10
}
This file can be converted to LSP values thanks to the parse method. The code below will correctly initialize the variables necessary to the model
function.
use json;
function input() {
data = json.parse("data.json");
nbItems = data["nbItems"];
weights = data["weights"];
prices = data["prices"];
knapsackBound = data["knapsackBound"];
}
After the problem has been solved, the dump method can be used to export the solution data.
function output() {
out = {
"value": knapsackValue.value,
"decisions": map[i in 0..nbItems-1](x[i].value)
};
json.dump(out, "out.json");
}
For more information about the module and parsing options, have a look at its reference.
Modeler API
The modeler API allows you to interact with the virtual machine powering the LSP language: developers with prototypes written in LSP will now be able to integrate them in Python, Java, C#, and C++ environments fully. With these new bindings, you can, for example:
- Load existing LSP files into modules and execute them.
- Interact with functions and variables contained in modules.
- Expose methods developed in your language of choice as LSP functions.
Thus, integrating the knapsack LSP program in a Python environment is now possible. First, we need to instantiate the API’s main class, the LSPModeler, which will enable us to interact with modules, maps, and functions. Given that the model above is available to us in a file named knapsack.lsp
, we can load the module into the modeler with the following snippet:
from localsolver.modeler import *
with LSPModeler() as modeler:
knapsack_module = modeler.load_module("knapsack.lsp")
This makes all variables and functions contained in knapsack.lsp
available to us through the knapsack_module
object (see the LSPModule reference). It also enables us to execute this module, which means running the model
function and starting the resolution process. As we have seen before, we need to initialize a few variables beforehand.
To set a value for integer variables like nbItems
and knapsackBound
, we can do the following:
# Declares a variable named `nbItems` and sets its value to 5.
knapsack_module["nbItems"] = 5
Next, we need to input the weights and the prices. They are both collections of numbers, so they should be objects of type LSPMap.
# `weights_data` is the data we want to input.
weights_data = [5, 4, 6, 3, 1]
# `weights_map` is the LSPMap that will hold the values in the LSP program.
weights_map = modeler.create_map()
for value in weights_data:
weights_map.append(value)
knapsack_module["weights"] = weights_map
With all variables set, we can create a solver and execute the module by calling knapsack_module.run(). This method replicates the behavior we would get by executing the module from the command line, and we can pass down arguments like lsTimeLimit
.
solver = modeler.create_solver()
knapsack_module.run(solver, "lsTimeLimit=10")
Once the resolution process is done, we can extract solution data:
# First we extract the LSExpression created within the LSP program.
obj_expression = knapsack_module["knapsackValue"]
# We can then extract the objective value using the regular LocalSolver Python API.
obj_value = obj_expression.value
Using the modeler API, you can thus benefit from the ease of modeling in LSP whilst still being able to integrate models in Python, Java, C#, and C++ applications.
Putting all this together
Below is an example of how an LSP model can be integrated into a Flask web app. It contains a single endpoint that takes JSON data as input, sets the correct variables in the LSPModule, and outputs the final objective value and decisions in JSON.
from flask import Flask, request, jsonify
from localsolver.modeler import *
app = Flask(__name__)
def create_map(modeler, data):
map_ = modeler.create_map()
for val in data:
map_.append(val)
return map_
@app.route("/", methods=["POST"])
def index():
data = request.json
with LSPModeler() as modeler:
module = modeler.load_module("knapsack.lsp")
module["nbItems"] = data["nbItems"]
module["knapsackBound"] = data["knapsackBound"]
module["weights"] = create_map(modeler, data["weights"])
module["prices"] = create_map(modeler, data["prices"])
solver = modeler.create_solver()
module.run(solver)
return jsonify({
"value": module["knapsackValue"].value,
"decisions": [x_i.value for _, x_i in module["x"]]
})
We are at your disposal to accompany you in the discovery of LocalSolver. Just ask for your free trial license by registering here. In the meantime, don’t hesitate to contact us for any further information or support.