Initializing help system before first use

Burglar - Formulating logical constraints


Type: Knapsack problem
Rating: 1 (simple)
Description: This example shows how to setup a simple knapsack type problem in which an item selection should be made maximally profitable under some weight restriction. We first solve a pure knapsack problem. Then, we refine the constraints by explicitly forbidding certain item combinations, thereby showing multiple ways how to create indicator constraints.
File(s): BinBurglar.java


BinBurglar.java
// (c) 2023-2024 Fair Isaac Corporation

import static com.dashoptimization.objects.Utils.scalarProduct;
import static java.util.stream.Collectors.toMap;

import java.util.Arrays;
import java.util.Locale;
import java.util.Map;

import com.dashoptimization.ColumnType;
import com.dashoptimization.DefaultMessageListener;
import com.dashoptimization.XPRSenumerations;
import com.dashoptimization.objects.Expression;
import com.dashoptimization.objects.Inequality;
import com.dashoptimization.objects.Variable;
import com.dashoptimization.objects.XpressProblem;

/**
 * Knapsack problem. This example shows how to setup a simple knapsack type
 * problem in which an item selection should be made maximally profitable under
 * some weight restriction. We first solve a pure knapsack problem. Then, we
 * refine the constraints by explicitly forbidding certain item combinations,
 * thereby showing multiple ways how to create indicator constraints.
 */
public class BinBurglar {

    private static final double[] values = new double[] { 15, 100, 90, 60, 40, 15, 10, 1 };

    private static final double[] weights = new double[] { 2, 20, 20, 30, 40, 30, 60, 10 };

    private static final String[] names = { "camera", "necklace", "vase", "picture", "tv", "video", "chest", "brick" };

    private static final double WTMAX = 102;

    public static void main(String[] args) {
        try (XpressProblem prob = new XpressProblem()) {
            /* Output all messages. */
            prob.callbacks.addMessageCallback(DefaultMessageListener::console);
            /* VARIABLES */
            Variable[] x = prob.addVariables(values.length)
                    /* 1 if we take item i; 0 otherwise */
                    .withType(ColumnType.Binary).withName(i -> names[i]).toArray();

            /* CONSTRAINT */
            /* Weight restriction */
            prob.addConstraint(scalarProduct(x, weights).leq(WTMAX));

            /* OBJECTIVE */
            /* Set objective: maximize total value */
            prob.setObjective(scalarProduct(x, values), XPRSenumerations.ObjSense.MAXIMIZE);
            prob.writeProb("BinBurglar.mps");
            /* Solve */
            prob.optimize();

            /*
             * Check the status, which should be optimal and the solve should be completed
             */
            if (prob.attributes().getSolStatus() != XPRSenumerations.SolStatus.OPTIMAL)
                throw new RuntimeException("optimization failed with status " + prob.attributes().getSolStatus());

            /* Print out the solution */
            double[] sol = prob.getSolution();
            System.out.println("Objective: " + prob.attributes().getObjVal());
            for (Variable var : x) {
                System.out.print(var.getName() + ":" + sol[var.getIndex()] + " ");
            }
            System.out.println();

            /* Create a map to look up the variables by name */
            Map<String, Variable> varByName = Arrays.stream(x).collect(toMap(v -> v.getName(), v -> v));

            /*
             * Now add constraints to consider the items vase and picture as one pair and
             * the items "tv" and "video" as a second pair so that the pairs are always
             * taken together
             */
            Variable vase = varByName.get("vase");

            /* x["vase"] == x["picture"] */
            prob.addConstraint(vase.eq(varByName.get("picture")));

            /* x["tv"] == x["video"] */
            prob.addConstraint(varByName.get("tv").eq(varByName.get("video")));

            /*
             * Logical constraint: Either take the first pair "vase" and "picture" or the
             * second pair "tv" and "video" (but not both pairs).
             */
            Expression tvPlusVideo = varByName.get("tv").plus(varByName.get("video"));

            /* x["vase"]=1 -> x["tv"]+x["video"]=0 */
            prob.addConstraint(vase.ifThen(tvPlusVideo.leq(0.0)));

            /*
             * x["vase"]=0 -> x["tv"]+x["video"]=2, this uses an alternative way to create
             * the indicator
             */
            Inequality enablePairTwo = prob.addConstraint(tvPlusVideo.geq(2.0));
            prob.setIndicator(vase, false, enablePairTwo);

            /*
             * By writing out this small problem we can easily verify that everything looks
             * as expected
             *
             * prob.writeProb("BinBurglarL.lp", "l");
             */

            /* Solve again */
            prob.optimize();

            /* Get results of solve */
            if (prob.attributes().getSolStatus() != XPRSenumerations.SolStatus.OPTIMAL)
                throw new RuntimeException("optimization failed with status " + prob.attributes().getSolStatus());

            /* Print out the solution */
            sol = prob.getSolution();
            System.out.printf(Locale.US, "Objective: %f%n", prob.attributes().getObjVal());
            for (Variable var : x) {
                System.out.printf(Locale.US, "%s: %g ", var.getName(), sol[var.getIndex()]);
            }
            System.out.println();
        }
    }
}

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