Initializing help system before first use

Exchanging data between an application and a model

In the previous sections we have seen how to obtain solution information and other data from a Mosel model after its execution. For the integration of a model into an application a flow of information in the opposite sense, that is, from the host application to the model, will often also be required, in particular if data are generated by the application that serve as input to the model. It is possible to write out this data to a (text) file or a database and read this file in from the model, but it is clearly more efficient to communicate such data in memory directly from the application to the model.

In this section we show two versions of our Burglar example where all input data is loaded from the application into the model, using dense and sparse data format respectively. The same communication mechanism, namely a combination of the two I/O drivers (see Section Generalized file handling for further detail) raw and mem, is also used to write back the solution from the model to the calling application.

An alternative communication mechanism is presented in Section Dynamic data. Instead of working with blocks of predefined size as in the previous cases, here data is passed through flows, allowing for dynamic sizing on the application level, a feature that is particularly useful for solution output with sparse data structures.

A separate example (Section Scalars) shows how to input and output scalar data.

Dense arrays

In the first instance we are going to consider a version of the `Burglar' model that corresponds to the very first version we have seen in Section The burglar problem where all arrays are indexed by the range set ITEMS = 1..8. In our C program ugiodense.c below, this corresponds to storing data in standard C arrays that are communicated to the Mosel model at the start of its execution.

#include <stdio.h>
#include "xprm_mc.h"

double vdata[8]={15,100,90,60,40,15,10, 1};   /* Input data: VALUE */
double wdata[8]={ 2, 20,20,30,40,30,60,10};   /* Input data: WEIGHT */
double solution[8];               /* Array for solution values */

int main()
{
 XPRMmodel mod;
 int i,result;
 char vdata_name[40];             /* File name of input data 'vdata' */
 char wdata_name[40];             /* File name of input data 'wdata' */
 char solution_name[40];          /* File name of solution values */
 char params[144];                /* Parameter string for model execution */

 if(XPRMinit())                   /* Initialize Mosel */
  return 1;

/* Prepare file names for 'initializations' using the 'raw' driver */
 sprintf(vdata_name, "noindex,mem:%p/%d", vdata, (int)sizeof(vdata));
 sprintf(wdata_name, "noindex,mem:%p/%d", wdata, (int)sizeof(wdata));
 sprintf(solution_name, "noindex,mem:%p/%d", solution, (int)sizeof(solution));

                                  /* Pass file names as execution param.s */
 sprintf(params, "VDATA='%s',WDATA='%s',SOL='%s'", vdata_name, wdata_name,
         solution_name);

 if(XPRMexecmod(NULL, "burglar6.mos", params, &result, &mod))
  return 2;                       /* Execute a model file */

 if((XPRMgetprobstat(mod)&XPRM_PBRES)!=XPRM_PBOPT)
  return 3;                       /* Test whether a solution is found */

/* Display solution values obtained from the model */
 printf("Objective value: %g\n", XPRMgetobjval(mod));
 for(i=0;i<8;i++)
  printf(" take(%d): %g\n", i+1, solution[i]);

 XPRMresetmod(mod);               /* Reset the model */

 return 0;
} 

In this example we use the raw I/O driver for communication between the application and the model it executes. Employing this driver means that data is saved in binary format. File names used with the raw driver have the form rawoption[,...],filename. The option noindex for this driver indicates that data is to be stored in dense format, that is, just the data entries without any information about the indices—this format supposes that the index set(s) is known in the Mosel model before data is read in. The filename uses the mem driver, this means that data is stored in memory. The actual location of the data is specified by giving the address of the corresponding memory block and its size.

The program above works with the following version of the `Burglar' model where the locations of input and output data are specified by the calling application through model parameters. Instead of printing out the solution in the model, we copy the solution values of the decision variables take into the array of reals soltake that is written to memory and will be processed by the host application.

model Burglar6
 uses "mmxprs"

 parameters
  VDATA = ''; WDATA = ''         ! Locations of input data
  SOL = ''                       ! Location for solution data output
  WTMAX = 102                    ! Maximum weight allowed
 end-parameters

 declarations
  ITEMS = 1..8                   ! Index range for items

  VALUE: array(ITEMS) of real    ! Value of items
  WEIGHT: array(ITEMS) of real   ! Weight of items

  take: array(ITEMS) of mpvar    ! 1 if we take item i; 0 otherwise
  soltake: array(ITEMS) of real  ! Solution values
 end-declarations

 initializations from 'raw:'
  VALUE as VDATA  WEIGHT as WDATA
 end-initializations

! Objective: maximize total value
 MaxVal:= sum(i in ITEMS) VALUE(i)*take(i)

! Weight restriction
 sum(i in ITEMS) WEIGHT(i)*take(i) <= WTMAX

! All variables are 0/1
 forall(i in ITEMS) take(i) is_binary

 maximize(MaxVal)                 ! Solve the MIP-problem

! Output solution to calling application
 forall(i in ITEMS) soltake(i):= getsol(take(i))

 initializations to 'raw:'
  soltake as SOL
 end-initializations

end-model 

Sparse arrays

Let us now take a look at the case where we use a set of strings instead of a simple range set to index the various arrays in our model. Storing the indices with the data values makes necessary slightly more complicated structures in our C program for the input and solution data. In the C program below (file ugiosparse.c), every input data entry defines both, the value and the weight coefficient for the corresponding index.

#include <stdio.h>
#include "xprm_mc.h"

const struct
{                                 /* Initial values for array 'data': */
 const char *ind;                 /*   index name */
 double val,wght;                 /*   value and weight data entries */
} data[]={{"camera",15,2}, {"necklace",100,20}, {"vase",90,20},
          {"picture",60,30}, {"tv",40,40}, {"video",15,30},
          {"chest",10,60}, {"brick",1,10}};

const struct
{                                 /* Array to receive solution values: */
 const char *ind;                 /*   index name */
 double val;                      /*   solution value */
} solution[8];

int main()
{
 XPRMmodel mod;
 int i,result;
 char data_name[40];              /* File name of input data 'data' */
 char solution_name[40];          /* File name of solution values */
 char params[96];                 /* Parameter string for model execution */

 if(XPRMinit())                   /* Initialize Mosel */
  return 1;

/* Prepare file names for 'initializations' using the 'raw' driver */
 sprintf(data_name, "slength=0,mem:%p/%d", data, (int)sizeof(data));
 sprintf(solution_name, "slength=0,mem:%p/%d", solution, (int)sizeof(solution));

                                  /* Pass file names as execution param.s */
 sprintf(params, "DATA='%s',SOL='%s'", data_name, solution_name);

 if(XPRMexecmod(NULL, "burglar7.mos", params, &result, &mod))
  return 2;                       /* Execute a model file */

 if((XPRMgetprobstat(mod)&XPRM_PBRES)!=XPRM_PBOPT)
  return 3;                       /* Test whether a solution is found */

/* Display solution values obtained from the model */
 printf("Objective value: %g\n", XPRMgetobjval(mod));
 for(i=0;i<8;i++)
  printf(" take(%s): %g\n", solution[i].ind, solution[i].val);

 XPRMresetmod(mod);

 return 0;
} 

The use of the two I/O drivers is quite similar to what we have seen before. We now pass on data in sparse format, this means that every data entry is saved together with its index (tuple). Option slength=0 of the raw driver indicates that strings are represented by pointers to null terminated arrays of characters (C-string) instead of fixed size arrays.

Similarly to the model of the previous section, the model burglar7.mos executed by the C program above reads and writes data from/to memory using the raw driver and the locations are specified by the calling application through the model parameters. Since the contents of the index set ITEMS is not defined in the model we have moved the declaration of the decision variables after the data input where the contents of the set is known, thus avoiding the creation of the array of decision variables as a dynamic array.

model Burglar7
 uses "mmxprs"

 parameters
  DATA = ''                        ! Location of input data
  SOL = ''                         ! Location for solution data output
  WTMAX = 102                      ! Maximum weight allowed
 end-parameters

 declarations
  ITEMS: set of string             ! Index set for items
  VALUE: array(ITEMS) of real      ! Value of items
  WEIGHT: array(ITEMS) of real     ! Weight of items
 end-declarations

 initializations from 'raw:'
  [VALUE,WEIGHT] as DATA
 end-initializations

 declarations
  take: array(ITEMS) of mpvar      ! 1 if we take item i; 0 otherwise
 end-declarations

! Objective: maximize total value
 MaxVal:= sum(i in ITEMS) VALUE(i)*take(i)

! Weight restriction
 sum(i in ITEMS) WEIGHT(i)*take(i) <= WTMAX

! All variables are 0/1
 forall(i in ITEMS) take(i) is_binary

 maximize(MaxVal)                  ! Solve the MIP-problem

! Output solution to calling application
 forall(i in ITEMS) soltake(i):= getsol(take(i))

 initializations to 'raw:'
  soltake as SOL
 end-initializations

end-model 

Dynamic data

The two examples of in-memory communication of dense and sparse data in the preceding sections have in commun that all data structures in the application, and in particular the structures to receive output data, are of fixed size. We therefore now introduce an alternative communication mechanism working with flows, that enables dynamic sizing of data structures on the application level, a feature that is particularly useful for solution output where effective data sizes are not known a priori. This communication mechanism is based on the callback I/O driver cb (see also Section Redirecting the Mosel output). The main body of our C program now looks as follows.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xprm_mc.h"

                                  /* Input values for data: */
char *ind[]={"camera", "necklace", "vase", "picture", "tv", "video",
             "chest", "brick"};              /* Index names */
double vdata[]={15,100,90,60,40,15,10, 1};   /* Input data: VALUE */
double wdata[]={ 2, 20,20,30,40,30,60,10};   /* Input data: WEIGHT */
int datasize=8;

struct SolArray
{                                 /* Array to receive solution values: */
 const char *ind;                 /*   index name */
 double val;                      /*   solution value */
};

struct SolArray *solution;
int solsize;

int main()
{
 XPRMmodel mod;
 int i,result;
 char data_name[40];              /* File name of input data 'data' */
 char solution_name[40];          /* File name of solution values */
 char params[96];                 /* Parameter string for model execution */

 if(XPRMinit())                   /* Initialize Mosel */
  return 1;

/* Prepare file names for 'initializations' using the 'cb' driver */
 sprintf(data_name, "cb:%p", cbinit_from);
 sprintf(solution_name, "cb:%p", cbinit_to);

                                  /* Pass file names as execution param.s */
 sprintf(params, "DATAFILE='%s',SOLFILE='%s'", data_name, solution_name);

 if(XPRMexecmod(NULL, "burglar13.mos", params, &result, &mod))
  return 2;                       /* Execute a model file */

 if((XPRMgetprobstat(mod)&XPRM_PBRES)!=XPRM_PBOPT)
  return 3;                       /* Test whether a solution is found */

/* Display solution values obtained from the model */
 printf("Objective value: %g\n", XPRMgetobjval(mod));
 for(i=0;i<solsize;i++)
  printf(" take(%s): %g\n", solution[i].ind, solution[i].val);

 XPRMresetmod(mod);

 return 0;
}

The function for dynamic output retrieval employs the Mosel library functions that we have already seen in Section Accessing modeling objects and solution values for models after their termination. The prototype of the function cbinit_to needs to be exactly as shown below.

int XPRM_RTC cbinit_to(XPRMcbinit cb, void *info, const char *label,
                       int type, XPRMalltypes *ref)
{
 XPRMarray solarr;
 XPRMset sets[1];
 int indices[1];
 XPRMalltypes rvalue;
 int ct;

 if(strcmp(label,"SOL")==0)
 {
  solarr=ref->array;

  solsize=XPRMgetarrsize(solarr);
  solution = (struct SolArray *)malloc(solsize * sizeof(struct SolArray));

  XPRMgetarrsets(solarr,sets);    /* Get the indexing sets
                                     (we know array has 1 dimension) */
  ct=0;
  XPRMgetfirstarrtruentry(solarr,indices); /* Get the first true index tuple */
  do
  {
   solution[ct].ind=XPRMgetelsetval(sets[0],indices[0],&rvalue)->string;
   XPRMgetarrval(solarr,indices,&rvalue);
   solution[ct].val=rvalue.real;
   ct++;
  } while(!XPRMgetnextarrtruentry(solarr,indices));
 }
 else
 {
  printf("Unknown output data item: %s %p\n", label, ref);
 }
 return 0;
}

The dynamic data input to a Mosel model uses a new set of dedicated library functions.

The format used to represent data is the same as the default text format used by initializations blocks. For example, the array definition

 mydata: [ ("ind1" 3) [5 1.2] ("ind2" 7) [4 6.5] ] 

is represented by the following sequence of function calls:

XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);     ! [
XPRMcb_sendctrl(cb, XPRM_CBC_OPENNDX, 0);     !   (
XPRMcb_sendstring(cb, "ind1", 0);             !     "ind1"
XPRMcb_sendint(cb, 3, 0);                     !     3
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSENDX, 0);    !   )
XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);     !   [
XPRMcb_sendint(cb, 5, 0);                     !     5
XPRMcb_sendreal(cb, 1.2, 0);                  !     1.2
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);    !   ]
XPRMcb_sendctrl(cb, XPRM_CBC_OPENNDX, 0);     !   (
XPRMcb_sendstring(cb, "ind2", 0);             !     "ind2"
XPRMcb_sendint(cb, 7, 0);                     !      7
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSENDX, 0);    !   )
XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);     !   [
XPRMcb_sendint(cb, 4, 0);                     !     4
XPRMcb_sendreal(cb, 6.5, 0);                  !     6.5
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);    !   ]
XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);    ! ]         

The last argument '0' in these functions indicates that data is to be processed not immediately but only once the queue of tokens is full.

For our example, we thus have the following function definition (again, the prototype of the callback function must be defined exactly to the form expected by Mosel):

int XPRM_RTC cbinit_from(XPRMcbinit cb, void *info, const char *label,
                         int type, void *ref)
{
 int i;

 if(strcmp(label,"DATA")==0)
 {
  XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);
  for(i=0;i<datasize;i++)
  {
   XPRMcb_sendctrl(cb, XPRM_CBC_OPENNDX, 0);
    XPRMcb_sendstring(cb, ind[i], -1, 0);
   XPRMcb_sendctrl(cb, XPRM_CBC_CLOSENDX, 0);
   XPRMcb_sendctrl(cb, XPRM_CBC_OPENLST, 0);
    XPRMcb_sendreal(cb, vdata[i], 0);
    XPRMcb_sendreal(cb, wdata[i], 0);
   XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);
  }
  XPRMcb_sendctrl(cb, XPRM_CBC_CLOSELST, 0);
  return 0;
 }
 else
 {
  fprintf(stderr,"Label `%s' not found.\n",label);
  return 1;
 }
}

The model file burglar13.mos receives through its run-time parameters the callback functions that are to be used for data input/output in the initializations sections. The definition of the mathematical model is the same as in the previous model version and left out in the listing below.

model Burglar13
 uses "mmxprs"

 parameters
  DATAFILE = ''                    ! Location of input data
  SOLFILE = ''                     ! Location for solution data output
  WTMAX = 102                      ! Maximum weight allowed
 end-parameters

 declarations
  ITEMS: set of string             ! Index set for items
  VALUE: array(ITEMS) of real      ! Value of items
  WEIGHT: array(ITEMS) of real     ! Weight of items
  soltake: array(ITEMS) of real    ! Solution values
 end-declarations

 initializations from DATAFILE
  [VALUE,WEIGHT] as "DATA"
 end-initializations

...

 initializations to SOLFILE
  soltake as "SOL"
 end-initializations

end-model

Scalars

Besides arrays one might also wish to simply exchange scalars between the calling application and a Mosel model. One way of passing the value of a scalar to a model is to define it as a model parameter and pass the new value as an execution parameter of the model (as shown in Section Parameters). Alternatively, we might read or write scalar values in initializations blocks similarly to what we have seen in the previous section for arrays.

Consider the following C program: there are three scalars, wmax, numitem, and objval. The value of the first should be read in by the Mosel model and the last two receive solution values from the optimization run in the model.

#include <stdio.h>
#include "xprm_mc.h"

int wmax=100;
int numitem;
double objval;

int main()
{
 XPRMmodel mod;
 int result;
 char wmax_name[40];              /* File name of input data 'wmax' */
 char num_name[40];               /* File name of output data 'num' */
 char sol_name[40];               /* File name of solution value */
 char params[160];                /* Parameter string for model execution */

 if(XPRMinit()) return 1;         /* Initialize Mosel */

/* Prepare file names for 'initializations' using the 'raw' driver */
 sprintf(wmax_name, "mem:%p/%d", &wmax, (int)sizeof(wmax));
 sprintf(num_name, "mem:%p/%d", &numitem, (int)sizeof(numitem));
 sprintf(solution_name, "mem:%p/%d", &objval, (int)sizeof(objval));

                                  /* Pass file names as execution param.s */
 sprintf(params, "WMAX='%s',NUM='%s',SOLVAL='%s'", wmax_name, num_name,
         sol_name);

 if(XPRMexecmod(NULL, "burglar12.mos", params, &result, &mod))
  return 2;                       /* Execute a model file */

 if((XPRMgetprobstat(mod)&XPRM_PBRES)!=XPRM_PBOPT)
  return 3;                       /* Test whether a solution is found */

/* Display solution values obtained from the model */
 printf("Objective value: %g\n", objval);
 printf("Total number of items: %d\n", numitem);

 XPRMresetmod(mod);

 return 0;
} 

The Mosel model takes as execution parameters the filenames (location in memory) of the three scalars. The value WTMAX is initialized from the data in the application and the two other locations are written to in the initializations to block at the end of the model.

model Burglar12
 uses "mmxprs"

 parameters
  NUM = ''                         ! Location for no. of items output
  SOLVAL = ''                      ! Location for objective value output
  WMAX = ''                        ! Maximum weight allowed
 end-parameters

 declarations
  WTMAX: integer                   ! Maximum weight allowed
  ITEMS = {"camera", "necklace", "vase", "picture", "tv", "video",
           "chest", "brick"}     ! Index set for items
  VALUE: array(ITEMS) of real      ! Value of items
  WEIGHT: array(ITEMS) of real     ! Weight of items
  soltake: array(ITEMS) of real    ! Solution values
 end-declarations

 VALUE :: (["camera", "necklace", "vase", "picture", "tv", "video",
            "chest", "brick"])[15,100,90,60,40,15,10,1]
 WEIGHT:: (["camera", "necklace", "vase", "picture", "tv", "video",
            "chest", "brick"])[2,20,20,30,40,30,60,10]

 initializations from 'raw:'
  WTMAX as WMAX
 end-initializations

 declarations
  take: array(ITEMS) of mpvar      ! 1 if we take item i; 0 otherwise
 end-declarations

! Objective: maximize total value
 MaxVal:= sum(i in ITEMS) VALUE(i)*take(i)

! Weight restriction
 sum(i in ITEMS) WEIGHT(i)*take(i) <= WTMAX

! All variables are 0/1
 forall(i in ITEMS) take(i) is_binary

 maximize(MaxVal)                  ! Solve the MIP-problem

! Print out the solution
 writeln("Solution:")
 forall(i in ITEMS)  writeln(" take(", i, "): ", getsol(take(i)))

! Output solution to calling application
 initializations to 'raw:'
  evaluation of getobjval as SOLVAL
  evaluation of round(sum(i in ITEMS) getsol(take(i))) as NUM
 end-initializations

end-model 

© 2001-2019 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.