Initializing help system before first use

Implementation with BCL

For the implementation of the variable fixing solution heuristic we work with the MIP 1 model from Chapter 11. Through the definition of the heuristic in a separate function we only make minimal changes to the model itself: before solving our problem with the standard call to the method mipOptimize we execute our own solution heuristic and the solution printing also has been adapted.

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

using namespace std;
using namespace ::dashoptimization;

#define MAXNUM 4                   // Max. number of shares to be selected

#define NSHARES 10                 // Number of shares
#define NRISK 5                    // Number of high-risk shares
#define NNA 4                      // Number of North-American shares

void solveHeur();

double RET[] = {5,17,26,12,8,9,7,6,31,21};  // Estimated return in investment
int RISK[] = {1,2,3,8,9};          // High-risk values among shares
int NA[] = {0,1,2,3};              // Shares issued in N.-America

XPRBprob p("FolioMIPHeur");        // Initialize a new problem in BCL
XPRBvar frac[NSHARES];             // Fraction of capital used per share
XPRBvar buy[NSHARES];              // 1 if asset is in portfolio, 0 otherwise

int main(int argc, char **argv)
{
 int s;
 XPRBexpr Risk,Na,Return,Cap,Num;

// Create the decision variables (including upper bounds for `frac')
 for(s=0;s<NSHARES;s++)
 {
  frac[s] = p.newVar("frac", XPRB_PL, 0, 0.3);
  buy[s] = p.newVar("buy", XPRB_BV);
 }

// Objective: total return
 for(s=0;s<NSHARES;s++) Return += RET[s]*frac[s];
 p.setObj(Return);                 // Set the objective function

// Limit the percentage of high-risk values
 for(s=0;s<NRISK;s++) Risk += frac[RISK[s]];
 p.newCtr(Risk <= 1.0/3);

// Minimum amount of North-American values
 for(s=0;s<NNA;s++) Na += frac[NA[s]];
 p.newCtr(Na >= 0.5);

// Spend all the capital
 for(s=0;s<NSHARES;s++) Cap += frac[s];
 p.newCtr(Cap == 1);

// Limit the total number of assets
 for(s=0;s<NSHARES;s++) Num += buy[s];
 p.newCtr(Num <= MAXNUM);

// Linking the variables
 for(s=0;s<NSHARES;s++) p.newCtr(frac[s] <= buy[s]);

// Solve problem heuristically
 p.setSense(XPRB_MAXIM);
 solveHeur();

// Solve the problem
 p.mipOptimize("");

// Solution printing
 if(p.getMIPStat()==4 || p.getMIPStat()==6)
 {
  cout << "Exact solution: Total return: " << p.getObjVal() << endl;
  for(s=0;s<NSHARES;s++)
   cout << s << ": " << frac[s].getSol()*100 << "%" << endl;
 }
 else
  cout << "Heuristic solution is optimal." << endl;

 return 0;
}

void solveHeur()
{
 XPRBbasis basis;
 int s, ifgsol;
 double solval, bsol[NSHARES],TOL;

 XPRSsetintcontrol(p.getXPRSprob(), XPRS_CUTSTRATEGY, 0);
                                   // Disable automatic cuts
 XPRSsetintcontrol(p.getXPRSprob(), XPRS_HEURSTRATEGY, 0);
                                   // Disable MIP heuristics
 XPRSsetintcontrol(p.getXPRSprob(), XPRS_PRESOLVE, 0);
                                   // Switch presolve off
 XPRSgetdblcontrol(p.getXPRSprob(), XPRS_FEASTOL, &TOL);
                                   // Get feasibility tolerance

 p.mipOptimize("l");               // Solve the LP-relaxation
 basis=p.saveBasis();              // Save the current basis

// Fix all variables `buy' for which `frac' is at 0 or at a relatively
// large value
 for(s=0;s<NSHARES;s++)
 {
  bsol[s]=buy[s].getSol();         // Get the solution values of `frac'
  if(bsol[s] < TOL)  buy[s].setUB(0);
  else if(bsol[s] > 0.2-TOL)  buy[s].setLB(1);
 }

 p.mipOptimize("c");               // Solve the MIP-problem
 ifgsol=0;
 if(p.getMIPStat()==4 || p.getMIPStat()==6)
 {                                 // If an integer feas. solution was found
  ifgsol=1;
  solval=p.getObjVal();            // Get the value of the best solution
  cout << "Heuristic solution: Total return: " << p.getObjVal() << endl;
  for(s=0;s<NSHARES;s++)
   cout << s << ": " << frac[s].getSol()*100 << "%" << endl;
 }

// Reset variables to their original bounds
 for(s=0;s<NSHARES;s++)
  if((bsol[s] < TOL) || (bsol[s] > 0.2-TOL))
  {
   buy[s].setLB(0);
   buy[s].setUB(1);
  }

 p.loadBasis(basis);        /* Load the saved basis: bound changes are
                               immediately passed on from BCL to the
                               Optimizer if the problem has not been modified
                               in any other way, so that there is no need to
                               reload the matrix */
 basis.reset();             // No need to store the saved basis any longer
 if(ifgsol==1)
  XPRSsetdblcontrol(p.getXPRSprob(), XPRS_MIPABSCUTOFF, solval+TOL);
                            // Set the cutoff to the best known solution
}

The implementation of the heuristic certainly requires some explanations.

In this example for the first time we use the direct access to Xpress Optimizer. To do so, we need to include the Optimizer header file xprs.h. The Optimizer functions are applied to the problem representation (of type XPRSprob) held by the Optimizer which can be retrieved with the method getXPRSprob of the BCL problem. For more detail on how to use the BCL and Optimizer libaries in combination the reader is refered to the `BCL Reference Manual'. The complete documentation of all Optimizer functions and parameters is provided in the `Optimizer Reference Manual'.

Parameters: The solution heuristic starts with parameter settings for the Xpress Optimizer. Switching off the automated cut generation (parameter XPRS_CUTSTRATEGY) and the MIP heuristics (parameter XPRS_HEURSTRATEGY) is optional, whereas it is required in our case to disable the presolve mechanism (a treatment of the matrix that tries to reduce its size and improve its numerical properties, set with parameter XPRS_PRESOLVE), because we interact with the problem in the Optimizer in the course of its solution and this is only possible correctly if the matrix has not been modified by the Optimizer.

In addition to the parameter settings we also retrieve the feasibility tolerance used by Xpress Optimizer: the Optimizer works with tolerance values for integer feasibility and solution feasibility that are typically of the order of 10-6 by default. When evaluating a solution, for instance by performing comparisons, it is important to take into account these tolerances.

The fine tuning of output printing mentioned in Chapter 10 can be obtained by setting the parameters XPRS_LPLOG and XPRS_MIPLOG (both to be set with function XPRSsetintcontrol).

Optimization calls: We use the optimization method mipOptimize with the argument "l", indicating that we only want to solve the top node LP relaxation (and not yet the entire MIP problem). To continue with MIP solving from the point where we have stopped the algorithm we use the argument "c".

Saving and loading bases: To speed up the solution process, we save (in memory) the current basis of the Simplex algorithm after solving the initial LP relaxation, before making any changes to the problem. This basis is loaded again at the end, once we have restored the original problem. The MIP solution algorithm then does not have to re-solve the LP problem from scratch, it resumes the state where it was `interrupted' by our heuristic.

Bound changes: When a problem has already been loaded into the Optimizer (e.g. after executing an optimization statement or following an explicit call to method loadMat) bound changes via setLB and setUB are passed on directly to the Optimizer. Any other changes (addition or deletion of constraints or variables) always lead to a complete reloading of the problem.

The program produces the following output. As can be seen, when solving the original problem for the second time the Simplex algorithm performs 0 iterations because it has been started with the basis of the optimal solution saved previously.

Reading Problem FolioMIPHeur
Problem Statistics
          14 (      0 spare) rows
          20 (      0 spare) structural columns
          49 (      0 spare) non-zero elements
Global Statistics
          10 entities        0 sets        0 set members
Maximizing MILP FolioMIPHeur
Original problem has:
        14 rows           20 cols           49 elements        10 globals
Will try to keep branch and bound tree memory usage below 14.8Gb
Starting concurrent solve with dual

 Concurrent-Solve,   0s
            Dual
    objective   dual inf
 D  14.066667   .0000000
------- optimal --------
Concurrent statistics:
      Dual: 5 simplex iterations, 0.00s
Optimal solution found

   Its         Obj Value      S   Ninf  Nneg   Sum Dual Inf  Time
     5         14.066667      D      0     0        .000000     0
Dual solved problem
  5 simplex iterations in 0s

Final objective                         : 1.406666666666667e+01
  Max primal violation      (abs / rel) :       0.0 /       0.0
  Max dual violation        (abs / rel) :       0.0 /       0.0
  Max complementarity viol. (abs / rel) :       0.0 /       0.0
All values within tolerances
 *** Search unfinished ***    Time:     0 Nodes:          0
Number of integer feasible solutions found is 0
Best bound is    14.066667

Starting root cutting & heuristics

 Its Type    BestSoln    BestBound   Sols    Add    Del     Gap     GInf   Time
a           13.100000    14.066667      1                  6.87
Heuristic search started
Heuristic search stopped
 *** Search completed ***     Time:     0 Nodes:          1
Number of integer feasible solutions found is 1
Best integer solution found is    13.100000
Best bound is    13.100014
Heuristic solution: Total return: 13.1
0: 20%
1: 0%
2: 30%
3: 0%
4: 20%
5: 30%
6: 0%
7: 0%
8: 0%
9: 0%
Maximizing MILP FolioMIPHeur
Original problem has:
        14 rows           20 cols           49 elements        10 globals
Will try to keep branch and bound tree memory usage below 14.8Gb
Starting concurrent solve with dual

 Concurrent-Solve,   0s
            Dual
    objective   dual inf
 D  14.066667   .0000000
------- optimal --------
Concurrent statistics:
      Dual: 0 simplex iterations, 0.00s
Optimal solution found

   Its         Obj Value      S   Ninf  Nneg   Sum Dual Inf  Time
     0         14.066667      D      0     0        .000000     0
Dual solved problem
  0 simplex iterations in 0s

Final objective                         : 1.406666666666667e+01
  Max primal violation      (abs / rel) :       0.0 /       0.0
  Max dual violation        (abs / rel) :       0.0 /       0.0
  Max complementarity viol. (abs / rel) :       0.0 /       0.0
All values within tolerances

Starting root cutting & heuristics

 Its Type    BestSoln    BestBound   Sols    Add    Del     Gap     GInf   Time
Heuristic search started
Heuristic search stopped

Starting tree search.
Deterministic mode with up to 8 running threads and up to 16 tasks.

    Node     BestSoln    BestBound   Sols Active  Depth     Gap     GInf   Time
 *** Search completed ***     Time:     0 Nodes:          5
Problem is integer infeasible
Number of integer feasible solutions found is 0
Best bound is    13.100001
Heuristic solution is optimal.

Observe that the heuristic found a solution of 13.1, and that the MIP optimizer without the heuristic could not find a better solution (hence the infeasible message). The heuristic solution is therefore optimal.


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