Modeler¶
Object-oriented C#/.NET APIs are provided for the virtual machine powering the LSP language. With only a few classes, you can load modules, interact with their variables and execute them. If you are not familiar with the LSP language, please have a look at the language reference for more information.
Create and execute a module
First, you have to create a LSPModeler
environment. As with
LocalSolver, we recommend using a “using” statement that will ensure the
native memory allocated for the modeler is correctly released when the
environment is discarded. LSPModeler
is the main class of the
Modeler library and allows you to create a module in one of two ways:
You can load a module from an existing LSP file with the method
LSPModeler.LoadModule
.Or you can create an empty module with the method
LSPModeler.CreateModule
.
Both of these methods return an instance of LSPModule
which
will enable you to interact with the module variables and functions. To be
runnable, a module must implement a model
function that will build a
LSModel
. You can then call LSPModule.Run
to start the
execution of the module. After the LocalSolver model has been built, the
resolution process will be started. You can also import arguments in a
module with the method LSPModule.ParseArguments
. This method imports
each passed argument as a global variable in the module:
using (LSPModeler modeler = new LSPModeler()) {
LSPModule module = modeler.LoadModule("my_lsp_file.lsp");
module.ParseArguments("lsIterationLimit=100", "lsTimeLimit=10");
module.Run();
}
Interacting with variables
You can interact with the variables inside a module thanks to getters and
setters on the LSPModule
class. Values can be obtained in their
native C# type or retrieved as LSPValue
which is a container
that can hold any type of value inside a module. For more information
on value types available in the modeler you can look at LSPType
.
You can create maps from the modeler instance with
LSPModeler.CreateMap
. A map is a data structure holding
(key, value) pairs that can also be used as an array. For more information
on maps you can look at the
map module.
Using external functions
You can use your own C# functions as
LSP functions in the modeler thanks to the
method LSPModeler.CreateFunction
. The signature of the external
function must conform to the delegate LSPFunctor
:
LSPValue MyLspFunction(LSPModeler modeler, IList<LSPValue> arguments) {
double result = arguments[0].AsDouble() + arguments[1].AsDouble();
return modeler.CreateDouble(result);
}
void Main() {
LSPFunction lspFunc = modeler.CreateFunction(MyLspFunction);
List<LSPValue> arguments = new List<LSPValue>();
arguments.Add(modeler.CreateDouble(1.5));
arguments.Add(modeler.CreateDouble(2.3));
LSPValue result = lspFunc.Call(arguments);
Console.WriteLine("result = " + result.AsDouble()); // prints "result = 3.8"
}
In the snippet above we declare an LSP function that takes two LSP doubles
as input, add them together and returns the result. We can then call the
function with LSPFunction.Call
to execute the function and
retrieve the result.
You can also assign the function to a variable in a module with
LSPModule.SetFunction
. After doing so, the function will be callable
within any function of the module.
Memory management
When you access a modeler object from the C# API, your C# program takes a
new reference to that modeler object (LSPMap
, LSPModule
,
LSPValue
or LSPFunction
are examples of references to
modeler objects).
A modeler object can only be destroyed if no more C# reference points to it. In other words, a C# reference on a modeler object prevents the object from being freed even if it is no longer used in the modeler. If you have to create, access and destroy many objects in your LSP programs, you may keep in memory objects that have become inaccessible within your LSP programs. You have 3 ways to limit this phenomenon:
Wait for the destruction of your LSPModeler object. All references are automatically freed when the modeler is released by using the
LSPModeler.Dispose
method on the LSPModeler instance. This is the simplest solution and the recommended solution if the interactions between the modeler and your LSP programs are limited to a few exchanges at the beginning/end of the resolution.Release specific references at any time by calling
LSPMap.Dispose
,LSPModule.Dispose
,LSPFunction.Dispose
orLSPValue.Dispose
.Release all references attached to a
LSPReferenceScope
. When you instantiate a new reference scope object by doingnew LSPReferenceScope(modeler)
, all references created from that point anywhere in the API will be automatically associated with that scope. Finally, a call toLSPReferenceScope.Dispose
will release all references registered in the scope and will restore the previous scope as the current one.
Note that invoking any method on a released reference will throw an exception. It is therefore important not to release your references too early and to keep them alive as long as you need them.
Also note that several references can point to the same object if you
call methods like LSPMap.GetMap
or LSPModule.GetFunction
several times. At each call, a new reference is created. This is not a
problem in itself, but try to minimize the number of calls to functions that
return objects of type LSPMap
, LSPModule
,
LSPValue
or LSPFunction
to reduce the number of
references.