Mosel Run Time Library
Topics covered in this chapter:
- General
- Post processing interface
- Debugger interface
- Handling of modules
- Using IO drivers for data exchange
The Mosel Run Time (xprm_rt) Library provides a set of functions that may be used to load models in the form of BIM files, execute them and access model objects.
Programs using this library must include the header file xprm_rt.h that defines the following types:
- XPRMmodel:
- reference to a model stored in core memory
- XPRMdsolib:
- reference to a dynamic shared object descriptor
- XPRMattrdesc:
- reference to an attribute descriptor
- XPRMmpvar:
- reference to a decision variable
- XPRMlinctr:
- reference to a linear constraint
- XPRMset:
- reference to a set
- XPRMlist:
- reference to a list
- XPRMarray:
- reference to an array
- XPRMproc:
- reference to a procedure or function
The following basic types are also defined for completeness:
- XPRMinteger:
- integer value (C type int)
- XPRMreal:
- real value (C type double)
- XPRMboolean:
- Boolean value (C type int: 0 = false, 1 = true)
- XPRMstring:
- text string value (C type const char *)
Note that all text strings handled by functions of this library are encoded in UTF-8. It is therefore required to convert text strings to alternate encodings when exchanging data with other libraries not working with UTF-8. In particular the C library supports either wide characters (wchar_t type) or the default system encoding (that depends on the localisation of the system). These encoding conversions can be achieved with the help of the XPRNLS library (please refer to the XPRNLS Reference Manual for further details).
General
Initialization and termination
Each program using the Mosel libraries must start with a call to XPRMinit. If a Mosel library is loaded and unloaded dynamically at run time, the termination function XPRMfinish must be called before unloading the library in order to release the resources Mosel is using.
Finish Mosel.
|
|
Release the memory allocated by XPRMgetlibpath.
|
|
Get default current working directory.
|
|
Get the location of the Mosel runtime library.
|
|
Get license error message.
|
|
Get the location of the translated messages.
|
|
Get the maximum depth of a stack dump.
|
|
Get the version number of Mosel.
|
|
Get version numbers.
|
|
Initialize Mosel.
|
|
Remove the Mosel temporary directory.
|
|
Set default current working directory.
|
|
Set the location of the translated messages.
|
|
Set execution restrictions.
|
|
Set the maximum depth of a stack dump.
|
Model management
The following functions are required to manipulate models loaded in core memory: loading, running or unloading a model, getting information. Several models may be loaded in a single session of Mosel and used alternatively: each function requires a model (type XPRMmodel) as parameter to designate on which of the loaded models the operation is to be performed. This object of type XPRMmodel is returned by the function XPRMloadmod when a model has been successfully read from a bim (= binary model) file—BIM files are produced by the Mosel compiler either by using the command line interpreter or with the Model Compiler Library.—.
Find a model by its name or order number.
|
|
Retrieve annotations of a model.
|
|
Get the list of prefixes for loading of packages.
|
|
Get a property of a model.
|
|
Enumerate dependencies of a model or package.
|
|
Get the next model.
|
|
Get the next module loaded for a model.
|
|
Check if a model is running.
|
|
Load a Binary Model file.
|
|
Reset a model.
|
|
Run a model.
|
|
Set the list of prefixes for loading of packages.
|
|
Set default input/output streams.
|
|
Stop a running model.
|
|
Terminate execution of a model.
|
|
Unload a model.
|
Post processing interface
The post processing interface gives easy access to the internal database of Mosel. This database is composed of all model objects that are defined in a BIM file (like constants) or created during the execution of a model (like arrays). Obviously the dynamically created objects are only available after the model has been run.
Note that the dictionary is not available if the model has been compiled with the option ``s'' (strip symbols) and no identifier has been explicitly published (refer to the description of the public qualifier in declarations): such a model cannot be accessed through the post processing interface.
Get a string representation from an external type reference.
|
|
Find an attribute descriptor from its name.
|
|
Find an identifier in the dictionary.
|
|
Find the code associated to a type.
|
|
Get an attribute of an entity.
|
|
Get the next annotated identifier in the dictionary.
|
|
Get the next attribute descriptor.
|
|
Get the next identifier in the dictionary.
|
|
Get the next parameter of the model.
|
|
Enumerate components of a problem type.
|
|
Enumerate control parameters of a package.
|
|
Get the next overloaded version of a procedure or function.
|
|
Enumerate requirements of a package.
|
|
Get the procedure/function information.
|
|
Get a property of a type.
|
Lists
Lists are an ordered collection of objects. The functions available here allows to get properties of a list (size and type) as well as enumerate all elements it contains.
Get the size of a list.
|
|
Get the type of a list.
|
|
Get the next element of a list.
|
|
Get the previous element of a list.
|
Sets
Sets are used to index arrays: any model using arrays also uses sets even if no set has been defined explicitly. Note that a range is a special case of a set of integers which contains all consecutive integers in a given interval.
Get the index of a set element.
|
|
Get the value of an element of a set.
|
|
Get the first index of a set.
|
|
Get the last index of a set.
|
|
Get the size of a set.
|
|
Get the type of a set.
|
Arrays
In Mosel, arrays are used to store any kind of object, including other arrays or sets. The type of the array is also the type of the collected objects. The storage class indicates how these objects are stored in memory. In most cases this information can be ignored as all functions accessing arrays automatically handle each special case.
The storage class is encoded in two bits:
- XPRM_GRP_DYN
- The array is a dynamic array: there is no range defined for its indexing sets ( i.e. there cannot be any ``out of range error'' for this array as the indexing sets may grow on demand).
- XPRM_GRP_GEN
- The array is a general (= dynamic bounded) array: the number of elements may be augmented up to the range limits specified at its creation.
Typically a ``sparse table'' uses a storage class of XPRM_GRP_DYN or XPRM_GRP_DYN|XPRM_GRP_GEN (dynamic or fixed ranges). The Mosel compiler may decide which storage class should be used for each array: even a ``dense table'' may be created using a storage class of XPRM_GRP_DYN if the model does not provide enough information for deciding the actual size of the array at compile time.
For dynamic arrays one may distinguish between logical and true entries. Assuming an array has been created with the range 1..5, but only entry 3 has been defined, this array has 5 logical entries but only a single true entry. This difference is mainly noticeable in the functions provided for enumerating arrays.
Note that at the library level all arrays are indexed by integers (negative value are allowed). To use text index values, the conversion from the text to the order number must be performed using the function XPRMgetelsetndx.
Check whether an index tuple of an array is valid.
|
|
Compare two index tuples.
|
|
Get the number of dimensions of an array.
|
|
Get the index sets of an array.
|
|
Get the size of an array.
|
|
Get the type of an array.
|
|
Get the value of an array entry.
|
|
Get the list of indices of the first entry of an array.
|
|
Get the list of indices of the first true entry of an array.
|
|
Get the list of indices of the last entry of an array.
|
|
Get the list of indices of the next entry of an array.
|
|
Get the list of indices of the next true entry of an array.
|
Records
Records are a special kind of user defined types that associate to a an entity a collection of fields. Thanks to the following functions one can enumerate these fields and get the value of a specific field of given record.
Get the value of a field of a record.
|
|
Get the next field of a record type.
|
Unions
Unions are a special kind of user defined types that can hold values of different types. Thanks to the following functions one can enumerate the compatible types of a union and get the value of such an entity.
Get the next compatible type of a union type.
|
|
Get the type and structure of the value stored in a union.
|
|
Get the type ID of the value stored in a union.
|
|
Get the value stored in a union.
|
Problems
Like all statements of a model, the routines presented in this section are executed in the context of an active problem. By default, at the beginning of the processing of a model an initial problem is created: the ``main problem''. After the end of the execution of a model, this particular problem is active but a different problem can be selected using the routine XPRMselectprob. The following functions enable the user to access various information related to linear constraints and decision variables created or used in the context of the active problem. With the exception of the XPRMexportprob function, all operations in this section require the problem to be loaded into an optimizer either explicitly (e.g. procedure `loadprob' of the module ``mmxprs'') or implicitly by using an optimization operation (e.g. procedure `maximize' of the module ``mmxprs'') in the model. If no problem is available (model not run, no constraint created by the model or problem not loaded in an optimizer) a specific default value is returned by each function.
Export the active problem to a file.
|
|
Get the activity value of a linear constraint.
|
|
Get the solution value of a linear constraint.
|
|
Get the row number of a linear constraint.
|
|
Get the dual value of a linear constraint.
|
|
Get the objective function value.
|
|
Get the problem status of a model.
|
|
Get the reduced cost value of a variable.
|
|
Get the slack value of a linear constraint.
|
|
Get the column number of a decision variable.
|
|
Get the solution value of a variable.
|
|
Select the active problem.
|
Miscellaneous
Send a control character to an initialization stream.
|
|
Send an integer value to an initialization stream.
|
|
Send a real value to an initialization stream.
|
|
Send a text string value to an initialization stream.
|
|
Convert a date into a Julian Day Number (JDN).
|
|
Release a memory block allocated by the mem: IO driver.
|
|
Convert a Julian Day Number (JDN) into a calendar date.
|
|
Expand a path name and check whether it can be accessed.
|
|
Get the current date and time.
|
Debugger interface
The Mosel debugger interface provides the necessary functionality for controling the execution of a program (execution step by step, breakpoints, access to local symbols, stack frame change) that may be used, for instance, to implement an interactive debugger. This interface relies on debugging information stored in the BIM file which is generated at compile time depending on compilation options (see Section Compilation):
- correspondence between a global symbol and its value: this information is available as long as the source is not compiled with option "s";
- correspondence between a local symbol (e.g. index of a loop or variable local to a function) and its value: this information is generated when model is compiled with option "g";
- correspondence between source code and compiled code: the source location information is also constructed if option "g" was used for compilation;
- tracing facility to enable the Mosel virtual machine to suspend execution at a specified location (breakpoint) or execute one statement at a time: as opposed to the previous features, this information requires insertion of instructions in the compiled code (and may alter the execution speed of a model). To enable this extension, option "G" has to be used when compiling the source model.
A model to be run through the debugger interface should be compiled with flag "g" or "G".
For the functions described below, the source location is indicated by means of line indices: each of these indices is associated to a statement, a data structure declaration or an end of subroutine (just before it returns). The function XPRMdbg_getlocation makes the correspondence between a line index and an actual source location (i.e. file name and line number). The first statement of the program has always index 0 and the total number of indices can be obtained using XPRMdbg_getnblndx. It is also possible to retrieve all indices at once using XPRMdbg_getlndx. The indices of the first and last statements of a function are returned by XPRMdbg_findprocblklndx.
The execution of a program normally terminates when an error occurs or simply when all instructions have been run. Using the function XPRMdbg_setbrkp, it is possible to specify locations in the program where execution must be suspended. From these breakpoints, one can examine current value of variables, install new breakpoints then continue or cancel execution for instance.
Before procedures (or functions) are called during execution of a program, the execution context of the system (mainly local symbols and a reference to the next instruction) is saved on top of a stack. This way, after the routine returns, the state of the machine can be restored and the execution resumed. When the execution of the program is suspended, it may be interesting to change the current position in the stack, or stack frame, in order to view variables that are not defined at the current level because they are declared by the calling procedure. This can be achieved using function XPRMdbg_setstacklev.
In order to use the debugger interface, the program has to be run with the function XPRMdbg_runmod: this special version of XPRMrunmod requires an extra parameter specifying a function reference, Mosel calls this function whenever the program has to be interrupted. If there is no error condition, the return value of the function decides whether execution should continue or not. During the interruption, most functions listed in this manual can be used to retrieve information about the current state of the program. Moreover, XPRMfindident returns references to locally defined symbols when called from the debugger interface.
Clear a breakpoint at the given line index.
|
|
Find the line index of a procedure or function.
|
|
Retrieve all line indices.
|
|
Get a source file location associated to a given line index.
|
|
Get the number of line indices.
|
|
Get the next local identifier in the dictionary.
|
|
Run a model through the debugger interface.
|
|
Set a breakpoint at the given line index.
|
|
Set the current stack frame to the specified level.
|
Handling of modules
The functionalities of Mosel may be extended by using native libraries or modules implemented as dynamic shared objects (DSO). The module manager of Mosel keeps a list of all loaded modules and maintains a list of references for each of them. Using the following functions it is possible to know which modules are currently loaded and what are the provided features, and to access the values of their control parameters.
Disable/enable automatic unloading of modules.
|
|
Find a DSO descriptor from a module name.
|
|
Unload unused dynamic shared objects.
|
|
Retrieve annotations defined by a module.
|
|
Get the current value of a control parameter.
|
|
Get the directory list where DSO files are searched for.
|
|
Get a property of a dynamic shared object.
|
|
Get next dynamic shared object.
|
|
Enumerate constants of a module.
|
|
Enumerate dependencies of a module.
|
|
Enumerate control parameters of a module.
|
|
Enumerate procedures and functions of a module.
|
|
Enumerate native types of a module.
|
|
Get the next IO driver in the list of available drivers.
|
|
Explicitly load the named module.
|
|
Declare a module as static.
|
|
Set the directory list where DSO files are stored.
|
Using IO drivers for data exchange
Mosel comes with a default set of IO drivers which are used as data source/destination. The selection of the driver is achieved via the file name in use: for instance file name "myfile" is a physical file handled by the operating system but "mem:myfile" is a block of memory managed by the mem driver. IO drivers are mainly used to interface specific data sources with Mosel (like odbc from the mmodbc module). In this context, each data source may require a dedicated driver that can be implemented in a user module through the Mosel NI (refer to the Mosel NI Reference Manual for further explanation). Drivers may also be employed to easily exchange information between the application running the Mosel Libraries and a model. In particular the predefined drivers cb, mem and raw are specifically designed for this purpose.
sysfd driver
Thanks to this driver, a file descriptor provided by the operating system may be used in place of a file. The general syntax of a file name for the sysfd driver is:
sysfd:OSfd
where OSfd is a numerical file descriptor (Posix) or a file handle (Windows). File descriptors are usually returned by C functions open or fileno (from a C-stream obtained with fopen) on Posix systems. Under Windows, file handles can be created using CreateFile or obtained with _get_osfhandle (from a C file descriptor) for instance. When a program starts, 3 files are automatically opened for input, output and errors; they are respectively associated to file numbers 0,1 and 2 (this applies to both Posix systems and Windows). Mosel uses these file decriptors as default streams.
Example:
XPRMsetdefstream(NULL,XPRM_F_ERROR,"sysfd:1"); /* redirect error to output stream */
cb driver
This driver allows using a function as a file. The general syntax of a file name for the cb driver is:
cb:funcaddr[/refval]
where funcaddr is the address of the callback function and the optional parameter refval is a pointer (both references must be expressed in hexadecimal). Depending on the type of stream to manage (i.e. a general stream or a for an initializations block) a specific function type as to be provided.
Handling of general streams
The expected function must have the following prototype:
long XPRM_RTC func(XPRMmodel model, void *ref, char *buf, unsigned long size);
Whenever data needs to be transferred, Mosel calls this function indicating the location (buf) and the size (size) of the buffer to use. The parameter ref is the information provided to Mosel during the opening of the file (refval above). The model reference may be NULL if the stream is used directly by Mosel (for instance for compilation). When the stream is open for writing, the return value of the function is ignored. If the corresponding output stream is open in text mode, the function is called at each end of line and the buffer can be seen as a NULL terminated character string (the size does not include the terminating character). When used for reading, the function should return the number of bytes actually copied into the buffer (0 means end of file).
Example:
long XPRM_RTC simpleout(XPRMmodel model, void *ref, char *buf, unsigned long size) { printf("OUT: %.*s",(int)size,buf); return 0; } ... char fname[32]; sprintf(fname, "cb:%p", simpleout); XPRMsetdefstream(NULL, XPRM_F_ERROR, fname); /* redirect error str. to 'simpleout' */ ...
Handling of initializations blocks
In the case of an initializations block, the expected function must be of the following form:
int XPRM_RTC func(XPRMcbinit cbinit, void *ref,const char *label, int type, XPRMalltypes *obj);
When executing an initializations from block, the function is called once for each label with the label to be initialized (label), its type (type) and a reference to the object (obj). The parameter ref is the information provided to Mosel during the opening of the file (refval above). The function must then send to Mosel the data to be used for the initialization of the object using routines XPRMcb_sendint, XPRMcb_sendreal, XPRMcb_sendstring and XPRMcb_sendctrl. The 3 first functions provide the basic type values while the last one is used to structure the data stream (i.e. delimit a list of array indices or a collection of values) in a similar fashion as in an ASCII initialization file.
Example:
int XPRM_RTC initfrom(XPRMcbinit cbinit, void *ref,const char *label, int type, XPRMalltypes *ref) { int i; if(strcmp(label,"I")==0) /* I:10 */ { XPRMcb_sendint(cbinit,10,0) } else if(strcmp(label,"S")==0) /* S:[1 2 3] */ { XPRMcb_sendctrl(cbinit,XPRM_CBC_OPENLST,0); for(i=1;i<=3;i++) XPRMcb_sendreal(cbinit,(double)i,0); XPRMcb_sendctrl(cbinit,XPRM_CBC_CLOSELST,0); } else if(strcmp(label,"A")==0) /* A:[(1) "a"] */ { XPRMcb_sendctrl(cbinit,XPRM_CBC_OPENLST,0); XPRMcb_sendctrl(cbinit,XPRM_CBC_OPENNDX,0); XPRMcb_sendint(cbinit,1,0); XPRMcb_sendctrl(cbinit,XPRM_CBC_CLOSENDX,0); XPRMcb_sendstring(cbinit,"a",1,0); XPRMcb_sendctrl(cbinit,XPRM_CBC_CLOSELST,0); } } /* The associated Mosel code: declarations I:integer S:set of real A:array(range) of string end-declarations initializations from INITFILE I S A end-initialisations */
Similarly, when executing an initializations to block, the function is called once for each label with the object reference, its type and associated label (in this case the first parameter is NULL). The user function can then inspect the object using the usual routines of the Mosel Runtime Library.
mem driver
With this driver, a block of memory is used as data source. Three different types of blocks are supported: named blocks can be used only from a model during its execution, are identified by a label and their allocation is dynamic. The second type uses a block of memory already allocated: it is characterized by an address and a size. With the third form the file name corresponds to a reference to a dedicated data structure to hold the properties of a memory block managed by Mosel.
The general syntax of a file name for the mem driver accessing a named block is:
mem:label[/minsize[/incstep]]
where label is an identifier whose first character is a letter and minsize an optional initial amount of memory to be reserved (size is expressed in bytes, in kilobytes with suffix "k" or in megabytes with suffix "m"). The memory block is allocated dynamically and resized as necessary. By default the size of the memory block is increased by pages of 4 kilobytes: the optional parameter incstep may be used to change this page size (i.e. the default setting is "label/0/4k"). The special value 0 modifies the allocation policy: instead of being increased of a fixed amount, the block size is doubled. In all cases unused memory is released when the file is closed.
When a named memory block is used in a model, it is possible to access the block of memory allocated by the driver by searching for the label in the model's dictionary: the function XPRMfindident returns a reference to an object of structure XPRM_STR_MEM that describes the location and size of the memory block.
The general syntax of a file name for the mem driver accessing a fixed block is:
mem:addr/size[/actualsize]
where addr and size identify the memory block (the pointer must be expressed in hexadecimal). Optionally a pointer to a size_t value may be provided (actualsize expressed in hexadecimal): when the stream is open for writing, this variable receives the size actually used by the operation (its value thus ranges between 0 and size). Moreover, if the stream is open in append mode, writing starts after the location indicated by this value. When the stream is open for reading, the value is used in place of size if it is smaller than this upper limit.
Example:
char blk[2048]; char fname[40]; size_t actualsize; sprintf(fname, "mem:%p/%u/%p", blk, (int)sizeof(blk), &actualsize); XPRMcompmod(NULL, "mymodel", fname, NULL); /* compile model to memory */ printf("BIM data uses %u bytes.\n", actualsize); mod=XPRMloadmod(fname, NULL); /* load BIM file from memory */
The last form is similar to first one except that the name of the block is replaced by an address prefixed by the & symbol:
mem:&addr[/minsize[/incstep]]
where addr is a reference (expressed in hexadecimal) to an XPRMmemblk data structure:
typedef struct { void *ref; /* Base address of the block */ size_t size; /* Size of the block */ } XPRMmemblk;
When the block is used for the first time, fields of the structure must be cleared: Mosel handles (re)allocation of the memory block when writing to the corresponding memory file. The function XPRMfreememblk can be used to release the memory allocated through this IO driver (deleting the file from the Mosel code has the same effect).
Example:
XPRMmemblk memblk; char fname[40]; sprintf(fname, "mem:&%p", &memblk); memset(&memblk,0,sizeof(XPRMmemblk)); XPRMcompmod(NULL, "mymodel", fname, NULL); /* compile model to memory */ mod=XPRMloadmod(fname, NULL); /* load BIM file from memory */ XPRMfreememblk(&memblk) /* release BIM file memory */
raw driver
The raw driver provides an implementation of the ``initializations blocks'' in binary mode: instead of translating information from/to text format, data is kept in its raw representation. Typically this driver will be combined with the mem driver in order to exchange arrays of data between the model and an application through memory without translation. The general syntax of a file name for the raw driver is:
raw:[noindex,align,noalign,append,all,slength=#]
When using the raw driver as a file for an initializations block, no actual data location is provided at the beginning of the block. The driver uses each label as a file name for locating data.
Example:
initializations from "raw:noindex" t as "datafile.bin" r as "mem:0x1234/456" end-initializations
Data transfer is achieved without conversion: 4 bytes for an integer, 8 bytes for a real, 1 byte for a Boolean, strings are of fixed size or just an address, external types are translated to strings (if ``tostr'' is available for the type) and anything else has the size of an address that is 4 or 8 bytes depending on the architecture. The option slength specifies the fixed length of strings, default value for this parameter is 16 (shorter strings are padded with 0 characters, longer strings are cut). The special value 0 implies that the address of the string is used.
If option append is specified, files open for writing are open in append mode.
Transfer of scalar is straightforward and sets are treated as a collection of consecutive scalars. The handling of arrays varies depending on the options: by default, each array element is preceded by its indices (for instance t(1,2) is stored or read as 1,2,t(1,2)). If option noindex is in use, only values are listed and if option all has been given, all elements of dynamic arrays are listed (by default: only existing elements).
The driver aligns data according to the processor architecture requirements assuming the starting address provided is aligned properly (for instance on Sparc processors real values [or doubles] are aligned on 8 bytes boundaries). Thanks to this property, it is safe to map data exchanged using this driver with the corresponding structure in the C language.
Example:
declarations a: array(integer,boolean) of real end-declarations ! the above declaration can be mapped to the following C-structure: ! struct { ! int ndx1 ! char ndx2 ! double a_ndx1_ndx2 }; ! This structure uses 13 bytes with an Intel processor and 16 on a Sparc
This behavior may be changed by using the align and noalign options (for instance for saving binary data to physical files, alignment is not necessary and uses more memory).
Options may be specified for each label individually: they have to be given as a list preceding the actual filename.
Example: the following model:
parameters DAT="" RES="" end-parameters declarations d:array(string) of real r:array(1..10) of real end-declarations initializations from "raw:" d as "slength=0,mem:"+DAT ! load data from memory location defined by DAT end-initializations ... initializations to "raw:" r as "noindex,mem:"+RES ! save results in memory location defined by RES end-initializations
can be used with the following C-source:
char params[128]; struct { const char *ndx; double v; } d[]={{"one",10}, {"two",0.5}}; double r[10]; sprintf(params, "DAT='%p/%u', RES='%p/%u'", d, sizeof(d), r, sizeof(r)); XPRMrunmod(mod, &result, params);
bin driver
Like the raw driver, the bin driver provides an implementation of the ``initializations blocks'' in binary mode. However, thanks to a structured and architecture independent data format the bin driver can handle cross platform files containing all records of an initialisations block. An application can generate and decode files using this format with the help of the bindrv library. Refer to the documentation of this library for further explanation.
© 2001-2022 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.