Initializing help system before first use

Implementing a solver callback

Example

Many programs, and in particular LP/MIP solvers, provide the possibility to interact with the program during its execution by means of callbacks. In terms of an example, we will show here how to implement an 'INTSOL' callback for Xpress Optimizer, that is, an etry point for calling a Mosel subroutine every time the solver has found a new MIP solution. The corresponding Mosel code might look as follows (notice that the Mosel subroutine is flagged as public in order to make it visible for external programs):

  public procedure intsol
    writeln("!!! New solution !!!")
   writeln("Solution: ", getobjval, "; ", x.sol, "; ", y.sol),
      "; obj=", getparam("myxp_lpobjval"))
  end-procedure

 ! Define the procedure 'intsol' as the solver INTSOL callback routine
  setcbintsol("intsol")

Implementation of callback handling

The handling of callbacks by the Mosel NI is not specific to the matrix / MIP solver interface, it can be applied for any external program that provides entry points for callbacks. In our case, the subroutine setcbintsol is declared via the following entry in the list of subroutines.

{"setcbintsol",2102,XPRM_TYP_NOT,1,"s",slv_lc_setcbintsol}

The implementation of the 'setcbintsol' routine in the module function slvlc_setcbintsol checks whether the specified subroutine has the expected format before saving its name in the problem structure.

static int slv_lc_setcbintsol(XPRMcontext ctx,void *libctx)
{
 s_slvctx *slctx;
 s_slvpb *slpb;
 XPRMalltypes result;
 const char *procname,*partyp;
 int nbpar,type;

 slctx=libctx;
 slpb=SLVCTX2PB(slctx);
 procname=XPRM_POP_REF(ctx);

 if(procname!=NULL)
 {                  /* The specified entity must be a procedure */
  if(XPRM_STR(mm->findident(ctx,procname,&result))!=XPRM_STR_PROC)
  {
   mm->dispmsg(ctx,"myxprs: Wrong subroutine type for callback `intsol'.\n");
   return RT_ERROR;
  }
  do
  {                 /* The specified procedure must not have any arguments */
   mm->getprocinfo(result.proc,&partyp,&nbpar,&type);
   if((type==XPRM_TYP_NOT)&&(nbpar==0)) break;
   result.proc=mm->getnextproc(result.proc);
  } while(result.proc!=NULL);
  if(result.proc==NULL)
  {
   mm->dispmsg(ctx,"myxprs: Wrong procedure type for callback `intsol'.\n");
   return RT_ERROR;
  }
  else
   slpb->cb_intsol=result.proc;
 }
 else
  slpb->cb_intsol=NULL;
 return RT_OK;
}

The problem structure s_slvpb has received a new field to store the callback reference:

 typedef struct SlvPb
	{
	 ...
	 XPRMproc cb_intsol;
	} s_slvpb; 

We also add a line for initializing this callback to the problem creation routine slv_pb_create after the creation of the actual problem:

 /* Define intsol callback */
 XPRSaddcbintsol(slpb->xpb,(void*)slv_cb_intsol,slpb,0);

The callback function slv_cb_intsol needs to have the prototype required by the solver library. In addition to the invocation of the Mosel procedure specified in the model via setcbintsol we also add the handling of user interrupts to this implementation.

static void XPRS_CC slv_cb_intsol(XPRSprob opt_prob,s_slvpb *slpb)
{
 XPRMalltypes result;
 XPRSprob xpb_save;

 if(slpb->cb_intsol!=NULL)
 {
  xpb_save=slpb->xpb;
  slpb->xpb=opt_prob;
  slpb->have=0;
  if(mm->callproc(slpb->saved_ctx,slpb->cb_intsol,&result)!=0)
  {
   mm->stoprun(slpb->saved_ctx);
   XPRSinterrupt(opt_prob,XPRS_STOP_CTRLC);
  }
  slpb->xpb=xpb_save;
 }
}