Initializing help system before first use

An overview of BCL in .NET

The .NET interface of BCL provides the full functionality of the C version, targeting .NET framework version 2.0 or higher. The C modeling objects, such as variables, constraints and problems, are again converted into classes, and their associated functions into methods of the corresponding class in .NET.

In .NET, the termwise definition of constraints is simplified by the overloading of the algebraic operators like '+`, '-`, '<=`, or '==` as in the C++ interface. With these operators constraints may be written in a form that is close to an algebraic formulation. Also, for printing output, it is possible to use both .NET native IO functions or the XPRBprob.printF BCL function (which corresponds to the BCL C XPRBprintf function).

The names of classes and methods have been adapted to .NET naming standards: all .NET classes that have a direct correspondence with modeling objects in BCL (namely XPRBprob, XPRBvar, XPRBctr, XPRBcut, XPRBsol, XPRBsos, XPRBbasis) take the same names, with the exception of XPRBidxset which becomes XPRBindexSetin .NET. The names of the methods are also changed by dropping the prefix XPRB and references to the type of the object, and each word is capitalized. For example, function XPRBgetvarname is turned into the method getName of class XPRBvar. Two exceptions are XPRBreadlinecb and XPRBreadarrlinecb which maintain these names as methods of the XPRBprob class and thus become XPRBprob.XPRBreadline and XPRBprob.XPRBreadarrline. The auto-completion feature in Visual Studio .NET can be used to obtain a full list of class methods and properties and prototypes of each method.

The BCL functionality is exposed through the XPRB and XPRBprob classes, which reside in the BCL namespace, which resides in the xprbdn.dll assembly.

Each source file that uses BCL should import the BCL namespace and should be compiled with a reference to xprbdn.dll. To add a reference to a Visual Studio .NET project, select Add Reference from the Project menu. Click the Browse button and locate xprbdn.dll in the XpressMP/bin folder.

Users of the .NET command line compilers, for example, csc for C#, or vbc for Visual Basic, can add a reference with the /reference option:

csc /reference:<path to xprbdn.dll> <your source file>

The C++ classes and their methods documented in section C++ class reference correspond to a large extend to the classes defined by the .NET interface, with some additional classes in the .NET version. A comprehensive documentation of the BCL .NET interface is available as a separate `HTML on-line documentation' located in subdirectory docs/bcl/bcl.net/HTML of the Xpress installation directory.

Example

An example of the use of BCL in .NET is the following, which again constructs the example described in Chapter Modeling with BCL. Contrary to the C and C++ versions, BCL .NET needs to be initialized explicitly by calling the static method XPRB.init().
BCL models can take up large amounts of memory therefore, if a BCL .NET model is embedded into an application, we recommend to explicitly release the resources used by the XPRBprob object, once it is no longer needed, by calling XPRB.Dispose()(particularly, the memory used by the underlying C structures that are not taken into account by the automated garbage collection in .NET). Alternatively, a problem can be reset to free up the memory used by the optimization and solution data without removing the problem definition itself.

using BCL;

namespace Examples
{
  public class xbexpl1
  {
      const int NJ  =  4;             /* Number of jobs */
      const int NT  = 10;             /* Time limit */

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

      static XPRBvar[] start = new XPRBvar[NJ];          /* Start times of jobs  */
      static XPRBvar[,] delta = new XPRBvar[NJ,NT];      /* Binaries for start times */
      static XPRBvar z;                  /* Maximum completion time (makespan) */

      static XPRBprob p;

      static void jobsModel()
      {
          XPRBexpr le;
          int j,t;

          /****VARIABLES****/
          /* Create start time variables */
          for(j=0;j<NJ;j++) start[j] = p.newVar("start");
          z = p.newVar("z",BCLconstant.XPRB_PL,0,NT); /* Declare the makespan variable */

          for(j=0;j<NJ;j++)              /* Declare binaries for each job  */
              for(t=0;t<(NT-DUR[j]+1);t++)
                  delta[j,t] = p.newVar("delta" + (j+1) + (t+1),BCLconstant.XPRB_BV);

          /****CONSTRAINTS****/
          for(j=0;j<NJ;j++)              /* Calculate maximal completion time */
              p.newCtr("Makespan", start[j]+DUR[j] <= z);

          p.newCtr("Prec", start[0]+DUR[0] <= start[2]);
          /* Precedence relation between jobs  */

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

          for(j=0;j<NJ;j++)              /* One unique start time for each job  */
          {
              le = new XPRBexpr(0);
              for(t=0;t<(NT-DUR[j]+1);t++)  le += delta[j,t];
              p.newCtr("One_" + (j+1), le == 1);
          }

          /****OBJECTIVE****/
          /* Define and set objective function */
          p.setObj(p.newCtr("OBJ", new XPRBrelation(z)));

          /****BOUNDS****/
          for(j=0;j<NJ;j++) start[j].setUB(NT-DUR[j]+1);
          /* Upper bounds on start time variables */
      }

      static 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(BCLconstant.XPRB_PR,10*(t+1));
                  /* Give highest priority to variables for earlier start times */

          p.setSense(BCLconstant.XPRB_MINIM);
          p.mipOptimize();                   /* Solve the problem as MIP */
          statmip = p.getMIPStat();       /* Get the MIP problem status */

          if((statmip == BCLconstant.XPRB_MIP_SOLUTION) ||
             (statmip == BCLconstant.XPRB_MIP_OPTIMAL))
          {  /* An integer solution has been found */
              Console.WriteLine("Objective: {0}", p.getObjVal());
              for(j=0;j<NJ;j++)
              {  /* Print the solution for all start times */
                System.Console.WriteLine("{0}: {1}", start[j].getName(), start[j].getSol());
                for(t=0;t<NT-DUR[j]+1;t++)
                  System.Console.Write("{0}: {1} ",delta[j,t].getName(),delta[j,t].getSol());
                System.Console.WriteLine();
              }
          }
      }

      public static void Main()
      {
         XPRB.init();                   /* Initialize BCL  */
         p = new XPRBprob("Jobs");      /* Create a new problem */
         jobsModel();                   /* Basic problem definition */
         jobsSolve();                   /* Solve and print solution */
         return;
      }
  }
}

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

 static XPRBsos[] set = new XPRBsos[NJ]; /* Sets regrouping start times for jobs */
 static XPRBprob p;

 public static void jobsModel()
 {

   ...
   for(j=0;j<NJ;j++)              /* Declare binaries for each job */
       for(t=0;t<(NT-DUR[j]+1);t++)
           delta[j,t] = p.newVar("delta" + (j+1) + (t+1),BCLconstant.XPRB_PL,0,1);

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

Branching directives for the SOSs are added as follows.

for(j=0;j<NJ;j++)
    set[j].setDir(BCLconstant.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 definition */
p.exportProb(BCLconstant.XPRB_MPS,"expl1");  /* Output matrix to MPS file */

Similarly to what has been shown for the problem formulation in C and C++, we may read data from file and use index sets in the problem formulation. Only a few changes and additions to the basic model formulation are required for the creation and use of index sets. However, if we want to read in a data file in the format accepted by the C functions XPRBreadlinecb and XPRBreadarrlinecb (that is, using '!' as commentary sign, and ',' as separators, and skip blanks and empty lines), we need to configure the data file access in .NET.

In the following program listing we leave out the method jobsSolve because it remains unchanged from the previous.

using System.IO;
using BCL;

namespace Examples
{
  public class xbexpl1i
  {
      const int MAXNJ = 4;             /* Max. number of jobs */
      const int NT  = 10;             /* Time limit */

      //Define XPRBDATAPATH to whatever folder you wish.
      const string XPRBDATAPATH = "../../data";
      const string DATAFILE = XPRBDATAPATH + "/jobs/durations.dat";

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

      static XPRBindexSet Jobs;	    /* Job names */
      static XPRBvar[] start;             /* Start times of jobs  */
      static XPRBvar[,] delta;            /* Binaries for start times */
      static XPRBvar z;                   /* Maximum completion time (makespan) */

      XPRBprob p;                         /* BCL problem */

      static void readData()
      {
          string name;
          FileStream file;
          StreamReader fileStreamIn;

           /* Create a new index set */
          Jobs = p.newIndexSet("jobs", MAXNJ);


          file = new FileStream(DATAFILE, FileMode.Open, FileAccess.Read);
          fileStreamIn = new StreamReader(file);

          object[] tempobj = new object[2];
          while((NJ<MAXNJ) &&
          	(p.XPRBreadarrline(fileStreamIn, 99, "{t} , {g} ", out tempobj, 1) == 2))
          {
              int dummy;
              name = (string)tempobj[0];
              DUR[NJ] = (double)tempobj[1];
              dummy = Jobs + name;
              NJ++;
          }

          fileStreamIn.Close();
          file.Close();

          System.Console.WriteLine("Number of jobs read: " + Jobs.getSize());
      }

      static void jobsModel()
      {
          XPRBexpr le;
          int j,t;

          /****VARIABLES****/
              /* Create start time variables (incl. bounds) */
          start = new XPRBvar[NJ];
          if(start==null)
          {
              System.Console.WriteLine("Not enough memory for 'start' variables.");
              return;
          }
          for (j = 0; j < NJ; j++)
          	  start[j] = p.newVar("start", BCLconstant.XPRB_PL, 0, NT - DUR[j] + 1);
          z = p.newVar("z",BCLconstant.XPRB_PL,0,NT);  /* Declare the makespan variable */

          delta = new XPRBvar[NJ, NT];
          for(j=0;j<NJ;j++)              /* Declare binaries for each job  */
              for(t=0;t<(NT-DUR[j]+1);t++)
                  delta[j,t] = p.newVar("delta"+Jobs.getIndexName(j)+"_"+(t+1),
                    BCLconstant.XPRB_BV);

          /****CONSTRAINTS****/
          for(j=0;j<NJ;j++)              /* Calculate maximal completion time */
              p.newCtr("Makespan", start[j]+DUR[j] <= z);

          p.newCtr("Prec", start[0]+DUR[0] <= start[2]);
              /* Precedence relation between jobs  */

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

          for(j=0;j<NJ;j++)              /* One unique start time for each job  */
          {
              le = new XPRBexpr(0);
              for(t=0;t<(NT-DUR[j]+1);t++)  le += delta[j,t];
              p.newCtr("One_" + (j+1), le == 1);
          }

          /****OBJECTIVE****/
          p.setObj(p.newCtr(z));                   /* Define and set objective function */

          jobsSolve();                   /* Solve the problem */
      }

      public static void Main()
      {
         XPRB.init();
         p = new XPRBprob("Jobs");      /* Create a new problem */
         readData();                    /* Read in the data */
         jobsModel();                   /* Basic problem definition */
      }
  }
}

QCQP Example

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

using BCL;

namespace Examples
{
  public class xbexpl1i
  {
      const int MAXNJ = 4;             /* Max. number of jobs */
      const int NT  = 10;             /* Time limit */

      //Define XPRBDATAPATH to whatever folder you wish.
      const string XPRBDATAPATH = "../../data";
      const string DATAFILE = XPRBDATAPATH + "/jobs/durations.dat";

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

      static XPRBindexSet Jobs;	    /* Job names */
      static XPRBvar[] start;             /* Start times of jobs  */
      static XPRBvar[,] delta;            /* Binaries for start times */
      static XPRBvar z;                   /* Maximum completion time (makespan) */

      XPRBprob p;                         /* BCL problem */

      static void readData()
      {
          string name;
          FileStream file;
          StreamReader fileStreamIn;

           /* Create a new index set */
          Jobs = p.newIndexSet("jobs", MAXNJ);


          file = new FileStream(DATAFILE, FileMode.Open, FileAccess.Read);
          fileStreamIn = new StreamReader(file);

          object[] tempobj = new object[2];
          while((NJ<MAXNJ) &&
          	(p.XPRBreadarrline(fileStreamIn, 99, "{t} , {g} ", out tempobj, 1) == 2))
          {
              int dummy;
              name = (string)tempobj[0];
              DUR[NJ] = (double)tempobj[1];
              dummy = Jobs + name;
              NJ++;
          }

          fileStreamIn.Close();
          file.Close();

          System.Console.WriteLine("Number of jobs read: " + Jobs.getSize());
      }

      static void jobsModel()
      {
          XPRBexpr le;
          int j,t;

          /****VARIABLES****/
              /* Create start time variables (incl. bounds) */
          start = new XPRBvar[NJ];
          if(start==null)
          {
              System.Console.WriteLine("Not enough memory for 'start' variables.");
              return;
          }
          for (j = 0; j < NJ; j++)
          	  start[j] = p.newVar("start", BCLconstant.XPRB_PL, 0, NT - DUR[j] + 1);
          z = p.newVar("z",BCLconstant.XPRB_PL,0,NT);  /* Declare the makespan variable */

          delta = new XPRBvar[NJ, NT];
          for(j=0;j<NJ;j++)              /* Declare binaries for each job  */
              for(t=0;t<(NT-DUR[j]+1);t++)
                  delta[j,t] = p.newVar("delta" + Jobs.getIndexName(j) + "_" + (t+1),
                    BCLconstant.XPRB_BV);

          /****CONSTRAINTS****/
          for(j=0;j<NJ;j++)              /* Calculate maximal completion time */
              p.newCtr("Makespan", start[j]+DUR[j] <= z);

          p.newCtr("Prec", start[0]+DUR[0] <= start[2]);
              /* Precedence relation between jobs  */

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

          for(j=0;j<NJ;j++)              /* One unique start time for each job  */
          {
              le = new XPRBexpr(0);
              for(t=0;t<(NT-DUR[j]+1);t++)  le += delta[j,t];
              p.newCtr("One_" + (j+1), le == 1);
          }

          /****OBJECTIVE****/
          p.setObj(p.newCtr(z));                   /* Define and set objective function */

          jobsSolve();                   /* Solve the problem */
      }

      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(BCLconstant.XPRB_PR,10*(t+1));
                  /* Give highest priority to variables for earlier start times */

          p.setSense(BCLconstant.XPRB_MINIM);
          p.mipOptimize();                   /* Solve the problem as MIP */
          statmip = p.getMIPStat();       /* Get the MIP problem status */

          if((statmip == BCLconstant.XPRB_MIP_SOLUTION) ||
             (statmip == BCLconstant.XPRB_MIP_OPTIMAL))
          {  /* An integer solution has been found */
            System.Console.WriteLine("Objective: " + p.getObjVal());
            for(j=0;j<NJ;j++)
            {                              /* Print the solution for all start times */
              System.Console.WriteLine(start[j].getName() + ": " + start[j].getSol());
              for(t=0;t<NT-DUR[j]+1;t++)
                System.Console.Write(delta[j,t].getName()+ ": "+delta[j,t].getSol()+" ");
              System.Console.WriteLine();
            }
          }
      }

      public static void Main()
      {
         XPRB.init();
         p = new XPRBprob("Jobs");      /* Create a new problem */
         readData();                    /* Read in the data */
         jobsModel();                   /* Basic problem definition */
      }
  }
}

Error handling

If an error occurs, BCL .NET behaves like the C interface, that is, it prints an error message and terminates the program. Alternatively, if BCL error handling is disabled by calling XPRBprob.setErrCtrl(0), then all error messages are sent to the user-defined error callback without terminating the program; the user can both check these error messages and the return codes of each method to verify if it has completed correctly. The only case where a BCLExceptions is raised is when an error occurs while constructing a BCLexpr object. Below we show a .NET implementation of the example of user error handling with BCL from Section User error handling. Other features demonstrated by this example include

  • redirection of the BCL output stream for the whole program and for an individual problem;
  • setting the BCL message printing level;
using System;
using BCL;

namespace Examples
{
  public class UGExpl3
  {
      public static int rtsbefore = 1;

      public void modexpl3(ref XPRBprob prob)
      {
          XPRBvar[] x = new XPRBvar[3];
          XPRBctr[] ctr = new XPRBctr[2];
          XPRBexpr cobj;
          int i;

          for(i=0;i<2;i++)
              x[i] = prob.newVar("x_"+i, BCLconstant.XPRB_UI, 0, 100);

          /* Create the constraints:
          C1: 2x0 + 3x1 >= 41
          C2:  x0 + 2x1  = 13 */
          XPRBexpr C1linexp = new XPRBexpr();

          XPRBexpr C2linexp = new XPRBexpr();
          C1linexp = 2 * x[0] + 3 * x[1];
          C2linexp = x[0] + 2 * x[1];
          prob.newCtr("C1", C1linexp >= 41);
          prob.newCtr("C2", C2linexp == 13);

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

          // x[3] = prob.newVar("x_2", BCLconstant.XPRB_UI, 10, 1);

          /* Objective: minimize x0+x1 */
          cobj = new XPRBexpr(0);
          for(i=0;i<2;i++) cobj += x[i];
          prob.setObj(prob.newCtr("OBJ", cobj));
          prob.setSense(BCLconstant.XPRB_MINIM); /* Set objective sense to minimization */
          prob.print();                          /* Print current problem definition */

          prob.lpOptimize();                     /* Solve the LP */
          prob.printF("Problem status: " + prob.getProbStat() +
              "  LP status: " + prob.getLPStat() + "  MIP status: " +
              prob.getMIPStat() + "\n");

          /* This problem is infeasible, that means the following command will fail.
           * It prints a warning if the message level is at least 2 */

          prob.printF("Objective: " + prob.getObjVal() + "\n");

          /* Print solution values */
          for(i=0;i<2;i++)
              prob.printF(x[i].getName() + ":" + x[i].getSol() + ", ");
          prob.printF("\n");
      }

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

      /**** User error handling function ****/
      public static void usererror(IntPtr prob, object vp, int num, int type, string t)
      {
          Exception eBCL = new Exception("Error in usererror().");
          System.Console.WriteLine("BCL error " +num+ ": " + t);
          if(type==BCLconstant.XPRB_ERR) throw eBCL;
      }

      /**** User printing function ****/
      public static void userprint(IntPtr prob, object vp, string msg)
      {
          /* Print 'BCL output' whenever a new output line starts,
          otherwise continue to print the current line. */
          if(rtsbefore==1) System.Console.Write("BCL output: " + msg);
          else System.Console.Write(msg);
          rtsbefore = (msg.Length>0 && msg[msg.Length-1]=='\n') ? 1 : 0;
      }

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

      // This is where one might add custom logging
      static void DoSomeErrorLogging(string msg)
      {
          Console.WriteLine("Here's an error message! {0}", msg);
      }

      public static int Main()
      {
          try
          {
              /* Switch to error handling by the user's program */
              XPRB.setErrCtrl(0); // no auto quit on error
              int initCode = XPRB.init();
              if (initCode != 0 && initCode != 32) // both values are valid
              {
                  DoSomeErrorLogging(Optimizer.XPRS.GetLicErrMsg());
                  return initCode;
              }
              UGExpl3 TestInstance = new UGExpl3();

              XPRBprob prob = new XPRBprob("EXPL3");
              if (!prob.isValid())
              {
                  DoSomeErrorLogging("Unable to create XPRBprob \"EXPL3\"");
                  return 1;
              }

              /* Set the printing flag. Try other values:
                      0 - no printed output, 1 - only errors,
                      2 - errors and warnings, 3 - all messages */
              prob.setMsgLevel(2);

              /* Define the printing callback function */
              prob.MessageCallbacks += new XPRBMessageCallback(userprint);

              try
              {
                  prob.ErrorCallbacks += new XPRBErrorCallback(usererror);
                  TestInstance.modexpl3(ref prob); /* Formulate and solve the problem */
                  System.Console.WriteLine("I'm about to exit cleanly");
                  return 0;
              }
              catch
              {
                  System.Console.WriteLine("I cannot build the problem");
                  return 1;
              }

          }
          catch
          {
              System.Console.WriteLine("I cannot create the problem");
              return 1;
          }
      }
  }
}

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