Initializing help system before first use

Adding MIP solutions to the Optimizer


Type: Programming
Rating: 3 (intermediate)
Description: At each node of the tree search a variable fixing heuristic is applied to a copy of the problem. If an integer solution is found for the modified (sub)problem then this solution is added to the original problem.
File(s): AddMipSol.java
Data file(s): addmipsol.mps


AddMipSol.java
/***********************************************************************
   Xpress Optimizer Examples
   =========================

   file AddMipSol.java
   ````````````
   Apply an LP rounding heuristic at each node and inject feasible
   solutions found.

   (c) 2020-2025 Fair Isaac Corporation
***********************************************************************/

import com.dashoptimization.AbstractOptNodeListener;
import com.dashoptimization.DefaultMessageListener;
import com.dashoptimization.XPRS;
import com.dashoptimization.XPRSoptNodeListener;
import com.dashoptimization.XPRSprob;
import static com.dashoptimization.XPRSenumerations.CutStrategy;

public class AddMipSol {

    /** Callback that runs a simple rounding heuristic at each node.
     *
     * The callback fixes all integer fractional variables to their rounded
     * values and solves the resulting problem. If that leads to a feasible
     * solution then that solution is injected back into the solver as a
     * new incumbent.
     */
    private static final class OptNode extends AbstractOptNodeListener {

        /** Problem name. */
        final String probName;
        /** Number of columns. */
        final int nCol;
        /** Number of MIP entities. */
        final int nMipEnt;
        /** Information about MIP entities. */
        final XPRSprob.MIPEntityInfo mipEntInfo;

        public OptNode(XPRSprob prob) {
            // Capture some global statistics that will be needed in the
            // heuristic.
            probName = prob.getProbName();
            nCol = prob.attributes().getCols();
            nMipEnt = prob.attributes().getMIPEnts();
            mipEntInfo = prob.getDiscreteCols();
        }

        /** Callback main method.
         * This is the method that is invoked by the solver.
         */
        public int optNodeEvent(XPRSprob prob, int feas) {
            // Only run once per node (since adding a solution will call
            // the callback to be fired again)
            if (prob.attributes().getCallbackCount_OptNode() > 1)
                return feas;

            // Only run the search for solutions with small numbers of
            // integer infeasibilities */
            if (prob.attributes().getMIPInfeas() > 0.25 * nMipEnt)
                return feas;

            fixedSearch(prob);

            return feas;
        }

        /** Run the heuristic that fixes all fractional integer variables
         * to their rounded values.
         */
        private void fixedSearch(XPRSprob parent) {
            // Create resources we need
            int[] bndInd = new int[nCol];
            double[] bndVal = new double[nCol];
            byte[] bndType = new byte[nCol];
            Thread me = Thread.currentThread(); // to get unique names

            // Create a problem containing the original problem
            try (XPRSprob prob = new XPRSprob(null)) {
                prob.copyProb(parent);
                prob.postSolve();

                // Reset the controls of the problem.
                prob.setDefaults();

                // Rename the problem so we don't get collisions with
                // temporary files produced by the optimizer
                prob.setProbname(probName + "_" + me);

                // Output all messages.
                prob.addMessageListener(DefaultMessageListener::console);

                // Get information from the solution at the current node
                // of 'parent' problem */
                boolean hasMipIncumbent = parent.attributes().getMIPSols() != 0;
                double incumbentObj = parent.attributes().getMIPObjVal();
                double[] x = parent.getCallbackSolution();

                // Initialise bound counter
                int nBnd = 0;
                double mipTol = parent.controls().getMIPTol();

                // Go through MIP entities
                for (int i = 0; i < nMipEnt; i++) {
                    // Test whether each is a binary or integer variable
                    if (mipEntInfo.coltype[i] == 'B' || mipEntInfo.coltype[i] == 'I') {
                        int j = mipEntInfo.colind[i];

                        // Fix variables that are fractional
                        double rounded = Math.round(x[j]);
                        if (Math.abs(rounded - x[j]) <= mipTol) {
                            continue;
                        }
                        bndInd[nBnd] = j;
                        bndVal[nBnd] = rounded;
                        bndType[nBnd] = 'B';
                        nBnd++;
                    }
                }

                // Instruct Optimizer to change the bounds
                prob.chgBounds(nBnd, bndInd, bndType, bndVal);
                System.out.println(me + ": After MIP search, " + nBnd +
                                   " binary and integer variables were fixed");

                // Ensure we only do a small search in the fixed problem and
                // use the current incumbent objective from the main problem
                prob.controls().setMaxNode(100);
                prob.controls().setMaxMIPSol(1);
                if (hasMipIncumbent) {
                    // Set a cut-off to help the fixed search
                    prob.controls().setMIPAbsCutoff(incumbentObj);
                }
                prob.controls().setMIPLog(3);

                // Read the basis for the LP relaxation to reduce run time
                prob.readBasis(probName, "");

                // Run the optimizer
                prob.mipOptimize();

                // If we found a solution then load it into the main problem
                if (prob.attributes().getMIPSols() > 0) {
                    x = prob.getSolution();
                    parent.addMipSol(nCol, x, null, null);
                }
            }
        }
    }

    public static void main(String[] args) {
        // Get problem name from command line or use a default problem.
        String model = args.length == 0 ? "../data/addmipsol" : args[0];

        try (XPRSprob prob = new XPRSprob(null)) {
            prob.readProb(model, "");
            prob.addMessageListener(DefaultMessageListener::console);
            // Register the callback that will perform heuristic search.
            prob.addOptNodeListener(new OptNode(prob));

            // Make the problem difficult for the optimizer to solve.
            prob.controls().setCutStrategy(CutStrategy.NONE);
            prob.controls().setHeurEmphasis(0);
            prob.controls().setSBBest(0);
            prob.controls().setMIPThreads(20);

            // Log every node in the branch-and-bound search.
            prob.controls().setMIPLog(3);

            // Solve the root node relaxation and write out the basis
            // (to reduce solve time of the fixed problems) */
            prob.mipOptimize("l");
            prob.writeBasis("", "");

            // Run the MIP search
            prob.mipOptimize("");

            System.out.printf("Optimal objective value: %f%n",
                              prob.attributes().getMIPObjVal());

        }
    }
}

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