/********************************************************
  Xpress-BCL Java Example Problems
  ================================

  file folioheur.java
  ```````````````````
  Modeling a small LP problem 
  to perform portfolio optimization.
   -- Heuristic solution --

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, 2003, rev. Dec. 2011
********************************************************/

import com.dashoptimization.*;

public class folioheur
{
 static final int MAXNUM = 4;      /* Max. number of different assets */
 static final int NSHARES = 10;    /* Number of shares */
 static final int NRISK = 5;       /* Number of high-risk shares */
 static final int NNA = 4;         /* Number of North-American shares */

 static final double[] RET = {5,17,26,12,8,9,7,6,31,21};
                                   /* Estimated return in investment  */
 static final int[] RISK = {1,2,3,8,9};  /* High-risk values among shares */
 static final int[] NA = {0,1,2,3};      /* Shares issued in N.-America */

 static XPRB bcl;
 static XPRBprob p;
 static XPRBvar[] frac;            /* Fraction of capital used per share */
 static XPRBvar[] buy;             /* 1 if asset is in portfolio, 0 otherwise */
 
 public static void main(String[] args) throws XPRSprobException, XPRSexception
 { 
  int s;
  XPRBexpr Risk,Na,Return,Cap,Num;

  bcl = new XPRB();                  /* Initialize BCL */
  XPRS.init();                       /* Initialize Xpress-Optimizer */
  p = bcl.newProb("FolioMIPHeur");   /* Create a new problem in BCL */

/* Create the decision variables */
  frac = new XPRBvar[NSHARES];
  buy = new XPRBvar[NSHARES];
  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 */
  Return = new XPRBexpr();
  for(s=0;s<NSHARES;s++) Return.add(frac[s].mul(RET[s])); 
  p.setObj(Return);                  /* Set the objective function */

/* Limit the percentage of high-risk values */
  Risk = new XPRBexpr();
  for(s=0;s<NRISK;s++) Risk.add(frac[RISK[s]]); 
  p.newCtr(Risk.lEql(1.0/3));

/* Minimum amount of North-American values */
  Na = new XPRBexpr();
  for(s=0;s<NNA;s++) Na.add(frac[NA[s]]); 
  p.newCtr(Na.gEql(0.5));

/* Spend all the capital */
  Cap = new XPRBexpr();
  for(s=0;s<NSHARES;s++) Cap.add(frac[s]); 
  p.newCtr(Cap.eql(1));
 
/* Limit the total number of assets */
  Num = new XPRBexpr();
  for(s=0;s<NSHARES;s++) Num.add(buy[s]);
  p.newCtr(Num.lEql(MAXNUM));

/* Linking the variables */
  for(s=0;s<NSHARES;s++) p.newCtr(frac[s].lEql(buy[s]));

/* Solve problem heuristically */
  p.setSense(XPRB.MAXIM);
  solveHeur();

/* Solve the problem */
  p.mipOptimize("");
 
/* Solution printing */
  if(p.getMIPStat()==XPRB.MIP_SOLUTION || p.getMIPStat()==XPRB.MIP_OPTIMAL)
  {
   System.out.println("Exact solution: Total return: " + p.getObjVal());
   for(s=0;s<NSHARES;s++) 
    System.out.println(s + ": " + frac[s].getSol()*100 + "%");
  }    
  else
   System.out.println("Heuristic solution is optimal.");

/* Delete the problem */
  p.finalize();              
  p=null;
 }
 
 static void solveHeur() throws XPRSprobException
 {
  XPRSprob op;
  XPRBbasis basis;
  int s, ifgsol;
  double solval,TOL;
  double[] fsol;

  op = p.getXPRSprob();                  /* Retrieve the Optimizer problem */
  op.setIntControl(XPRS.CUTSTRATEGY, 0); /* Disable automatic cuts */
  op.setIntControl(XPRS.PRESOLVE, 0);    /* Switch presolve off */
  TOL = op.getDblControl(XPRS.FEASTOL);  /* Get feasibility tolerance */

  p.mipOptimize("l");              /* Solve the LP-problem */
  basis=p.saveBasis();             /* Save the current basis */

/* Fix all variables `buy' for which `frac' is at 0 or at a relatively
   large value */
  fsol = new double[NSHARES];
  for(s=0;s<NSHARES;s++)
  {
   fsol[s]=frac[s].getSol();       /* Get the solution values of `frac' */
   if(fsol[s] < TOL)  buy[s].setUB(0);
   else if(fsol[s] > 0.2-TOL)  buy[s].setLB(1);
  }

  p.mipOptimize("c");              /* Continue solving the MIP-problem */
  ifgsol=0; solval=0;
  if(p.getMIPStat()==XPRB.MIP_SOLUTION || p.getMIPStat()==XPRB.MIP_OPTIMAL)
  {                                /* If an integer feas. solution was found */
   ifgsol=1;
   solval=p.getObjVal();           /* Get the value of the best solution */
   System.out.println("Heuristic solution: Total return: " + p.getObjVal());
   for(s=0;s<NSHARES;s++) 
    System.out.println(s + ": " + frac[s].getSol()*100 + "%");  
  }

/* Reset variables to their original bounds */
  for(s=0;s<NSHARES;s++)
   if((fsol[s] < TOL) || (fsol[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 = null;             /* No need to store the saved basis any longer */
  if(ifgsol==1)
   op.setDblControl(XPRS.MIPABSCUTOFF, solval+TOL);
                            /* Set the cutoff to the best known solution */
 } 
}
