/********************************************************
  Xpress-BCL Java Example Problems
  ================================

  file folioqp.java
  `````````````````
  Modeling a small QP problem
  to perform portfolio optimization.
   -- 1. QP: minimize variance
      2. MIQP: limited number of assets ---

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, 2003, rev. Dec. 2011
********************************************************/

import com.dashoptimization.*;
import java.io.*;
import java.lang.*;

public class folioqp {
    static final String DATAFILE = System.getProperty("XPRBDATA") +
        "/GS/foliocppqp.dat";

    static final int TARGET = 9;      /* Target yield */
    static final int MAXNUM = 4;      /* Max. number of different assets */
    static final int NSHARES = 10;    /* Number of 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[] NA = {0,1,2,3};   /* Shares issued in N.-America */
    static double[][] VAR;            /* Variance/covariance matrix of
                                         estimated returns */

    private static void readData() throws IOException {
        int s,t;
        FileReader datafile=null;
        StreamTokenizer st=null;

        VAR = new double[NSHARES][NSHARES];

        /* Read `VAR' data from file */
        datafile=new FileReader(DATAFILE);   /* Open the data file */
        st=new StreamTokenizer(datafile); /* Initialize the stream tokenizer */
        st.commentChar('!');              /* Use the character '!' for comments */
        st.eolIsSignificant(true);        /* Return end-of-line character */
        st.parseNumbers();                /* Read numbers as numbers (not strings) */

        for(s=0;s<NSHARES;s++) {
            do {
                st.nextToken();
            } while(st.ttype==st.TT_EOL);    /* Skip empty lines and comment lines */
            for(t=0;t<NSHARES;t++) {
                if(st.ttype != st.TT_NUMBER) break;
                VAR[s][t] = st.nval;
                st.nextToken();
            }
        }

        datafile.close();
    }

    public static void main(String[] args) throws Exception {
        int s,t;
        XPRBexpr Risk,Na,Return,Cap,Num,Variance;
        XPRBvar[] frac;                  /* Fraction of capital used per share */
        XPRBvar[] buy;                   /* 1 if asset is in portfolio, 0 otherwise */

        try (XPRBprob p = new XPRBprob("FolioQP")) { /* Initialize BCL and create a new problem */
            readData();                     /* Read data from file */

            /***** First problem: unlimited number of assets *****/

            /* Create the decision variables */
            frac = new XPRBvar[NSHARES];
            for(s=0;s<NSHARES;s++) frac[s] = p.newVar("frac", XPRB.PL, 0, 0.3);

            /* Objective: mean variance */
            Variance = new XPRBexpr();
            for(s=0;s<NSHARES;s++)
                for(t=0;t<NSHARES;t++) Variance.add(frac[s].mul(frac[t]).mul(VAR[s][t]));
            p.setObj(Variance);              /* Set the objective function */

            /* 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));

            /* Target yield */
            Return = new XPRBexpr();
            for(s=0;s<NSHARES;s++) Return.add(frac[s].mul(RET[s]));
            p.newCtr(Return.gEql(TARGET));

            /* Solve the problem */
            p.setSense(XPRB.MINIM);
            p.lpOptimize("");

            /* Solution printing */
            System.out.println("With a target of " + TARGET + " minimum variance is "
                               + p.getObjVal());
            for(s=0;s<NSHARES;s++)
                System.out.println(s + ": " + frac[s].getSol()*100 + "%");

            /***** Second problem: limit total number of assets *****/

            /* Create the decision variables */
            buy = new XPRBvar[NSHARES];
            for(s=0;s<NSHARES;s++) buy[s] = p.newVar("buy", XPRB.BV);

            /* 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 the problem */
            p.mipOptimize("");

            /* Solution printing */
            System.out.println("With a target of " + TARGET + " and at most "
                               + MAXNUM + " assets, minimum variance is "
                               + p.getObjVal());
            for(s=0;s<NSHARES;s++)
                System.out.println(s + ": " + frac[s].getSol()*100 + "% (" +
                                   buy[s].getSol() + ")");

        }
    }
}
