| |||||||||||
Adding MIP solutions to the Optimizer 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.
Source Files By clicking on a file name, a preview is opened at the bottom of this page.
Data Files AddMipSol.java /*********************************************************************** Xpress Optimizer Examples ========================= file AddMipSol.java ```````````` Apply an LP rounding heuristic at each node and inject feasible solutions found. (c) 2020-2024 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()); } } } | |||||||||||
© Copyright 2024 Fair Isaac Corporation. |