Initializing help system before first use

An overview of BCL in C++

The C++ interface of BCL provides the full functionality of the C version except for the data input, output and error handling for which the corresponding C functions may be used. The C modeling objects, such as variables, constraints and problems, are converted into classes, and their associated functions into methods of the corresponding class in C++.

To use the C++ version of BCL, the C++ header file must be included at the beginning of the program (and not the main BCL header file xprb.h).

#include "xprb_cpp.h"

Using C++, the termwise definition of constraints is even easier. This has been achieved by overloading the algebraic operators like '+`, '-`, '<=`, or '==`. With these operators constraints may be written in a form that is close to an algebraic formulation.

It should be noted that the names of classes and methods have been adapted to C++ naming standards: All C++ classes that have a direct correspondence with modeling objects in BCL (namely XPRBprob, XPRBvar, XPRBctr, XPRBcut, XPRBsol, XPRBsos, XPRBindexSet, XPRBbasis) take the same names, with the exception of XPRBindexSet. In the names of the methods the prefix XPRB has been dropped, as have been references to the type of the object. For example, function XPRBgetvarname is turned into the method getName of class XPRBvar.

All C++ classes of BCL are part of the namespace dashoptimization. To use the (short) class names, it is recommended to add the line

using namespace ::dashoptimization;

at the beginning of every program that uses the C++ classes of BCL.

C++ functions can be used together with C functions, for instance when printing program output or using Xpress Optimizer functions. However, it is not possible to mix BCL C and C++ objects in a program.

Example

An example of use of BCL in C++ is the following, which constructs the scheduling example described in Chapter Modeling with BCL:

#include <iostream>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define NJ    4                // Number of jobs
#define NT   10                // Time limit

double DUR[] = {3,4,2,2};      // Durations of jobs

XPRBvar start[NJ];             // Start times of jobs
XPRBvar delta[NJ][NT];         // Binaries for start times
XPRBvar z;                     // Max. completion time

XPRBprob p("Jobs");            // Initialize BCL & a new problem

void jobsModel()
{
 XPRBexpr le;
 int j,t;
                               // Create start time variables
 for(j=0;j<NJ;j++) start[j] = p.newVar("start");
 z = p.newVar("z",XPRB_PL,0,NT);  // Makespan variable

 for(j=0;j<NJ;j++)             // Binaries for each job
  for(t=0;t<(NT-DUR[j]+1);t++)
   delta[j][t] =
          p.newVar(XPRBnewname("delta%d%d",j+1,t+1),XPRB_BV);

 for(j=0;j<NJ;j++)             // Calculate max. completion time
  p.newCtr("Makespan", start[j]+DUR[j] <= z);
                               // Precedence relation betw. jobs
 p.newCtr("Prec", start[0]+DUR[0] <= start[2]);

 for(j=0;j<NJ;j++)             // Linking start times & binaries
 {
  le=0;
  for(t=0;t<(NT-DUR[j]+1);t++)  le += (t+1)*delta[j][t];
  p.newCtr(XPRBnewname("Link_%d",j+1), le == start[j]);
 }

 for(j=0;j<NJ;j++)             // Unique start time for each job
 {
  le=0;
  for(t=0;t<(NT-DUR[j]+1);t++)  le += delta[j][t];
  p.newCtr(XPRBnewname("One_%d",j+1), le == 1);
 }

 p.setObj(z);                 // Define and set objective

 for(j=0;j<NJ;j++) start[j].setUB(NT-DUR[j]+1);
                              // Upper bounds on "start" var.s
}

void jobsSolve()
{
 int j,t,statmip;

 for(j=0;j<NJ;j++)
  for(t=0;t<NT-DUR[j]+1;t++)
   delta[j][t].setDir(XPRB_PR,10*(t+1));
     // Give highest priority to var.s for earlier start times

 p.setSense(XPRB_MINIM);
 p.mipOptimize();             // Solve the problem as MIP
 statmip = p.getMIPStat();    // Get the MIP problem status
 if((statmip == XPRB_MIP_SOLUTION) ||
    (statmip == XPRB_MIP_OPTIMAL))
 {                  // An integer solution has been found
  cout << "Objective: " << p.getObjVal() << endl;
  for(j=0;j<NJ;j++)
  {                 // Print the solution for all start times
   cout << start[j].getName() << ": " << start[j].getSol();
   cout << endl;
  }
 }
}

int main(int argc, char **argv)
{
  jobsModel();                // Problem definition
  jobsSolve();                // Solve and print solution
  return 0;
}

The definition of SOS is similar to the definition of constraints.

XPRBsos set[NJ];

void jobsModel()
{
 ...
 for(j=0;j<NJ;j++)            // Variables for each job
  for(t=0;t<(NT-DUR[j]+1);t++)
   delta[j][t] =
       p.newVar(XPRBnewname("delta%d%d",j+1,t+1),XPRB_PL,0,1);

 for(j=0;j<NJ;j++)            // SOS definition
 {
  le=0;
  for(t=0;t<(NT-DUR[j]+1);t++) le += (t+1)*delta[j][t];
  set[j] = p.newSos("sosj",XPRB_S1,le);
 }
}

Branching directives for the SOSs are added as follows.

for(j=0;j<NJ;j++) set[j].setDir(XPRB_DN);
                            // First branch downwards on sets

Adding the following two lines during or after the problem definition will print the problem to the standard output and export the matrix to a file respectively.

 p.print();                // Print out the problem def.
 p.exportProb(XPRB_MPS,"expl1");
                           // Output matrix to MPS file

Similarly to what has been shown for the problem formulation in C, we may read data from file and use index sets in the problem formulation. The following changes and additions to the basic model formulation are required for the creation of index sets based on data input from file. The function jobsSolve is left out in this listing since it remains unchanged from the previous one.

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define MAXNJ 4             // Max. number of jobs
#define NT   10             // Time limit

int NJ = 0;                 // Number of jobs read in
double DUR[MAXNJ];          // Durations of jobs

XPRBindexSet Jobs;          // Names of Jobs
XPRBvar *start;             // Start times of jobs
XPRBvar **delta;            // Binaries for start times
XPRBvar z;                  // Max. completion time

XPRBprob p("Jobs");         // Initialize BCL & a new problem

void readData()
{
 char name[100];
 FILE *datafile;
                            // Create a new index set
 Jobs = p.newIndexSet("jobs", MAXNJ);
                            // Open data file for read access
 datafile=fopen("durations.dat","r");
        // Read in all (non-empty) lines up to the end of the file
 while(NJ<MAXNJ &&
       XPRBreadlinecb(XPRB_FGETS, datafile, 99, "T,d", name, &DUR[NJ]))
 {
  Jobs += name;             // Add job to the index set
  NJ++;
 }
 fclose(datafile);          // Close the input file
 cout << "Number of jobs read: " << Jobs.getSize() << endl;
}

void jobsModel()
{
 XPRBexpr le;
 int j,t;
                            // Create start time variables with bounds
 start = new XPRBvar[NJ];
 if(start==NULL)
 { cout << "Not enough memory for 'start' variables." << endl;
   exit(0); }
 for(j=0;j<NJ;j++)
  start[j] = p.newVar("start",XPRB_PL,0,NT-DUR[j]+1));
 z = p.newVar("z",XPRB_PL,0,NT);  // Makespan variable

 delta = new XPRBvar*[NJ];
 if(delta==NULL)
 { cout << "Not enough memory for 'delta' variables." << endl;
   exit(0); }
 for(j=0;j<NJ;j++)          // Binaries for each job
 {
  delta[j] = new XPRBvar[NT];
  if(delta[j]==NULL)
  { cout <<"Not enough memory for 'delta_j' variables." << endl;
    exit(0); }
  for(t=0;t<(NT-DUR[j]+1);t++)
   delta[j][t] =
     p.newVar(XPRBnewname("delta%s_%d",Jobs[j],t+1), XPRB_BV);

 for(j=0;j<NJ;j++)          // Calculate max. completion time
  p.newCtr("Makespan", start[j]+DUR[j] <= z);
                            // Precedence relation betw. jobs
 p.newCtr("Prec", start[0]+DUR[0] <= start[2]);

 for(j=0;j<NJ;j++)          // Linking start times & binaries
 {
  le=0;
 for(t=0;t<(NT-DUR[j]+1);t++)  le += (t+1)*delta[j][t];
  p.newCtr(XPRBnewname("Link_%d",j+1), le == start[j]);
 }

 for(j=0;j<NJ;j++)          // Unique start time for each job
 {
  le=0;
  for(t=0;t<(NT-DUR[j]+1);t++)  le += delta[j][t];
  p.newCtr(XPRBnewname("One_%d",j+1), le == 1);
 }

 p.setObj(z);               // Define and set objective
 jobsSolve();               // Solve the problem

 delete [] start;
 for(j=0;j<NJ;j++) delete [] delta[j];
 delete [] delta;
}

int main(int argc, char **argv)
{
 readData();                // Read in the data
 jobsModel();               // Problem definition
 return 0;
}

QCQP Example

The following is an implementation with BCL C++ of the QCQP example described in Section Example:

#include <iostream>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

#define N 42
double CX[N], CY[N], R[N];

...                                     // Initialize the data arrays

int main(int argc, char **argv)
{
 int i,j;
 XPRBvar x[N],y[N];
 XPRBexpr qe;
 XPRBctr cobj, c;
 XPRBprob prob("airport");         // Initialize a new problem in BCL

/**** VARIABLES ****/
 for(i=0;i<N;i++)
  x[i] = prob.newVar(XPRBnewname("x(%d)",i+1), XPRB_PL, -10, 10);
 for(i=0;i<N;i++)
  y[i] = prob.newVar(XPRBnewname("y(%d)",i+1), XPRB_PL, -10, 10);

/****OBJECTIVE****/
// Minimize the total distance between all points
 qe=0;
 for(i=0;i<N-1;i++)
  for(j=i+1;j<N;j++) qe+= sqr(x[i]-x[j])+sqr(y[i]-y[j]);
 cobj = prob.newCtr("TotDist", qe);
 prob.setObj(cobj);                    // Set objective function

/**** CONSTRAINTS ****/
// All points within given distance of their target location
 for(i=0;i<N;i++)
  c = prob.newCtr("LimDist", sqr(x[i]-CX[i])+sqr(y[i]-CY[i]) <= R[i]);

/****SOLVING + OUTPUT****/
 prob.setSense(XPRB_MINIM);            // Choose sense of optimization
 prob.lpOptimize();                    // Solve the problem

 cout << "Solution: " << prob.getObjVal() << endl;
 for(i=0;i<N;i++)
 {
  cout << x[i].getName() << ": " << x[i].getSol() << ", ";
  cout << y[i].getName() << ": " << y[i].getSol() << endl;
 }

 return 0;
}

Error handling

The default behavior of BCL in the case of an error is to output a message and terminate the program. However, in C++ applications it may be more convenient to raise exceptions instead of simply exiting from the program. With the BCL C++ interface the user has the possibility to disable the standard 'exit on error' behavior replacing it, for instance, by C++ exceptions.

The C++ program below implements the example of user error handling from Section User error handling. The default error handling of BCL is disabled (function XPRBseterrctrl) and the error handling callback is defined to raise C++ exceptions in the case of an error—the BCL C++ interface uses the callback functions of the BCL C library. When using the BCL C functions with BCL C++ objects we need to employ their C representation (obtained with method getCRef).

Besides the user error handling this example also shows how to work with the user message printing callback to redirect the BCL output to a user-defined callback function (this includes output from BCL and anything printed through XPRBprintf). By setting the BCL message printing level (method setMsgLevel) you can control the amount of information output by BCL.

#include <iostream>
#include <string>
#include "xprb_cpp.h"

using namespace std;
using namespace ::dashoptimization;

class bcl_exception
{
 public:
   string msg;
   int code;
   bcl_exception(int c,const char *m)
   {
    code=c;
    msg=string(m);
    cout << "EXCP:" << msg << "\n";
   }
};

/**** User error handling function ****/
void XPRB_CC usererror(xbprob* prob, void *vp, int num, int type,
                       const char *t)
{
 throw bcl_exception(num, t);
}

/**** User printing function ****/
void XPRB_CC userprint(xbprob* prob, void *vp, const char *msg)
{
 static int rtsbefore=1;

    /* Print 'BCL output' whenever a new output line starts,
       otherwise continue to print the current line. */
 if(rtsbefore)
  cout << "BCL output: " << msg;
 else
  cout << msg;
 rtsbefore=(msg[strlen(msg)-1]=='\n');
}

/***********************************************************************/

void modexpl3(XPRBprob &p)
{
 XPRBvar x[3];
 XPRBlinExp le;
 int i;

 for(i=0;i<2;i++) x[i]=p.newVar(XPRBnewname("x_%d",i), XPRB_UI, 0, 100);

                /* Create the constraints:
                   C1: 2x0 + 3x1 >= 41
                   C2:  x0 + 2x1  = 13 */
 p.newCtr("C1", 2*x[0] + 3*x[1] >= 41);
 p.newCtr("C2", x[0] + 2*x[1] == 13);

// Uncomment the following line to cause an error in the model that
// triggers the user error handling:

// x[2]=p.newVar("x_2", XPRB_UI, 10,1);

 le=0;
 for(i=0;i<2;i++) le += x[i];    // Objective: minimize x0+x1
 p.setObj(le);                   // Select objective function
 p.setSense(XPRB_MINIM);         // Set objective sense to minimization

 p.lpOptimize();                 // Solve the LP
 XPRBprintf(p.getCRef(), "problem status: %d  LP status: %d  MIP status: %d\n",
    p.getProbStat(), p.getLPStat(), p.getMIPStat());

// This problem is infeasible, that means the following command will fail.
// It prints a warning if the message level is at least 2
 XPRBprintf(p.getCRef(), "Objective: %g\n", p.getObjVal());

 for(i=0;i<2;i++)                // Print solution values
  XPRBprintf(p.getCRef(), "%s:%g, ", x[i].getName(), x[i].getSol());
 XPRBprintf(p.getCRef(), "\n");
}

/***********************************************************************/

int main()
{
 XPRBprob *p;

 XPRBseterrctrl(0);     // Switch to error handling by the user's program
 XPRB::setMsgLevel(2);       // Set the printing flag. Try other values:
                             // 0 - no printed output, 1 - only errors,
                             // 2 - errors and warnings, 3 - all messages
                        // Define the callback functions:
 XPRBdefcbmsg(NULL, userprint, NULL);
 XPRBdefcberr(NULL, usererror, NULL);

 try
 {
  p=new XPRBprob("Expl3");   // Initialize a new problem in BCL
 }
 catch(bcl_exception &e)
 {
  cout << e.code << ":" << e.msg;
  return 1;
 }

 try
 {
  modexpl3(*p);              // Formulate and solve the problem
 }
 catch(bcl_exception &e)
 {
  cout << e.code << ":" << e.msg << "\n";
  return 2;
 }
 catch(const char *m)
 {
  cout << m << "\n";
  return 3;
 }
 catch(...)
 {
  cout << "other exception\n";
  return 4;
 }
 return 0;
} 

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