Hexaly Modeler
Object-oriented Python APIs are provided for the virtual machine powering the
Hexaly modeling language. With only a few classes, you can load modules,
interact with their variables and execute them. If you are not familiar with the
modeling language, please have a look at the
language reference for more information.
Create a module
First, you have to create an HexalyModeler
environment. As with
the main Hexaly Optimizer object, we recommend using a “with” statement that
will ensure the native memory allocated for the modeler is correctly
released when the environment is discarded. HexalyModeler
is
the main class of the Modeler library and allows you to create a module in
one of two ways:
Both of these methods return an instance of HxmModule
which
will enable you to interact with the module variables and functions.
Launch a module
There are two launch modes for a module:
Optimization mode: This is the classic and default mode for the modeler.
At a very least, you must implement a model
method in your module that
will build an HxModel. You can then call HxmModule.run()
to
start the execution of the module. This function takes as first argument a
Hexaly Optimizer instance that you have to create beforehand using the
HexalyModeler.create_optimizer()
method. After the
Hexaly Optimizer model has been built, the resolution process will be
started automatically.
Note that the run method also accepts as a parameter a list of arguments
that will be passed as global variables to your module:
with HexalyModeler () as modeler :
optimizer = modeler . create_optimizer ()
module = modeler . load_module ( "module_name" , "my_file.hxm" )
module . run ( optimizer , "hxIterationLimit=100" , "hxTimeLimit=10" )
Main mode: In this mode, the modeler behaves like a classical programming
language. To use this mode, you have to implement a function named
main
in which you are free to do anything you want without being
limited by the formalism of the functions input
, model
, param
,
display
and output
. You can then call
HxmModule.run_main()
to start the execution of the module.
Note that unlike the optimization mode, it is your responsibility to
manually create the optimizer instances, close your models and launch the
resolutions. In return, you are free to run several successive resolutions
or none at all if you just want to use Hexaly Modeler for its pure
programming features.
For more details on the differences between the optimization and the main
mode, read Main mode .
Interacting with variables
The class HxmModule
behaves like a dictionnary and you can
retrieve, set or create new variables that will be exposed in the modeler:
module = ...
print ( module [ "hxIterationLimit" ])
module [ "hxIterationLimit" ] = 100000
module [ "myVariable" ] = "spam, egg and cheese"
module [ "customData" ] = - 45.2
# Removing a variable, reset its value to "nil" in the modeler
del module [ "hxIterationLimit" ]
Under the hood, HxmModule
overloads the special methods
HxmModule.__getitem__()
, HxmModule.__setitem__()
and HxmModule.__delitem__()
.
These methods can receive or return python values that are transparently
transformed to their modeler equivalent. The following python types are
supported: boolean, integers, floating point numbers, strings and the
special value None (which is transposed to nil in the modeling language).
If simple values are not sufficient for your usage, you can also create
array-like or dictionary-like structures with
HexalyModeler.create_map()
. For more information on maps you can
look at the map module .
Using external functions
You can expose your own Python functions to Hexaly Modeler and turns them
into Hexaly functions thanks to the
method HexalyModeler.create_function()
. The Python function
must take a modeler instance as first argument, and modeler values as
subsequent arguments. The function can return a value that will be converted
and transmitted to the calling modeler:
def my_hxm_function ( modeler , arg1 , arg2 ):
return arg1 + arg2
hxm_func = modeler . create_function ( my_hxm_function )
result = hxm_func ( 1.5 , 2.3 )
print ( "result = {} " . format ( result )) # prints "result = 3.8"
In the snippet above we declare a function that takes two doubles as input,
adds them together and returns the result. We can then call the function
with HxmFunction.__call__()
to execute the function and retrieve
the result.
You can also assign the function to a variable in a module with
HxmModule.__setitem__()
. After doing so, the function will be
callable within any function of the module.
Memory management
When you access a modeler object from the Python API, your Python program
takes a new reference to that modeler object (HxmMap
,
HxmModule
or HxmFunction
are examples of references to
modeler objects).
A modeler object can only be destroyed if no more Python reference points
to it. In other words, a Python 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 Hexaly Modeler
programs, you may keep in memory objects that have become inaccessible
within your code.
We advise you not to rely on the destructors of python objects (__del__
)
to ensure a proper release of these references. Instead, use one of the 3
methods described below that are based on the with
statement. They are
much more explicit and safer:
Wait for the destruction of your HexalyModeler object. All references
are automatically freed when the modeler is released by using the
special with
statement on your HexalyModeler
instance. This
is the simplest solution and the recommended solution if the
interactions between your Python code and Hexaly Modeler are limited to
a few exchanges at the beginning/end of the resolution.
Release specific references at any time by using the with
statement
on objects HxmMap
, HxmModule
, HxmFunction
or
HexalyOptimizer
. For instance:
with modeler . create_map () as map :
map . append ( 42 )
# Reference to 'map' is freed at the end of the ``with`` block
Release all references attached to a HxmReferenceScope
.
When you instantiate a new reference scope object by doing
with HxmReferenceScope(modeler) as scope:
, all references created
from that point anywhere in the API will be automatically associated
with that scope. At the end of the with
statement, all references
registred in the scope will be released and the previous scope will be
restored and set 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.
Note also that several references can point to the same object if you query
repeatedly the same key of a map or a module. 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
HxmMap
, HxmModule
or HxmFunction
to reduce the
number of references.