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.cpp


BinBurglar.cpp
// (c) 2024-2025 Fair Isaac Corporation
#include <stdexcept> // For throwing exceptions
#include <xpress.hpp>

using namespace xpress;
using namespace xpress::objects;
using xpress::objects::utils::scalarProduct;

/*
 * 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
 * add more constraints by explicitly forbidding certain item combinations,
 * thereby showing multiple ways how to create indicator constraints.
 */

std::vector<double> values = {15, 100, 90, 60, 40, 15, 10, 1};
std::vector<double> weights = {2, 20, 20, 30, 40, 30, 60, 10};
std::vector<std::string> names = {"camera", "necklace", "vase",  "picture",
                                  "tv",     "video",    "chest", "brick"};
int MAX_WEIGHT = 102;

int main() {
  try {
    // Create a problem instance with verbose messages printed to Console
    XpressProblem prob;
    prob.callbacks.addMessageCallback(XpressProblem::console);

    // Binary variables, 1 if we put item i in the knapsack, 0 if not
    std::vector<Variable> x = prob.addVariables(values.size())
                                  .withType(ColumnType::Binary)
                                  .withName([&](auto i) { return names[i]; })
                                  .toArray();

    // Weight restriction on the knapsack
    prob.addConstraint(scalarProduct(x, weights) <= double(MAX_WEIGHT));

    // Objective: Maximze total value
    prob.setObjective(scalarProduct(x, values), xpress::ObjSense::Maximize);

    // Optimize
    // prob.writeProb("BinBurglar.lp");
    prob.optimize();

    // Check the solution status
    if (prob.attributes.getSolStatus() != xpress::SolStatus::Optimal) {
      std::ostringstream oss;
      oss << prob.attributes
                 .getSolStatus(); // Convert xpress::SolStatus to String
      throw std::runtime_error("Problem not solved to optimality: " +
                               oss.str());
    }

    // Print out the solution
    std::cout << std::endl << "*** Solution ***" << std::endl;
    std::cout << "Objective: " << prob.attributes.getObjVal() << std::endl;
    std::vector<double> sol = prob.getSolution();
    for (Variable v : x)
      std::cout << v.getName() << " = " << v.getValue(sol) << std::endl;
    std::cout << std::endl;

    /*
     * Now to show some additionaly functionaly, we 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
     */
    // Create a map (using for_each and lambda function) to facilitate easy look
    // up of the variables by name
    std::map<std::string, Variable> nameToVariableMap;
    std::for_each(x.begin(), x.end(), [&](Variable var) {
      nameToVariableMap[var.getName()] = var;
    });

    // Add constraint x["vase"] == x["picture"]
    prob.addConstraint(nameToVariableMap["vase"] ==
                       nameToVariableMap["picture"]);
    // Add constraint x["tv"] == x["video"]
    prob.addConstraint(nameToVariableMap["tv"] == nameToVariableMap["video"]);

    /*
     * Logical constraint: Take exactly one of the two pairs of variables (i.e.
     * take either the first pair "vase" and "picture" or the second pair "tv"
     * and "video", but not both pairs)
     */
    // x["vase"]=1 -> x["tv"]+x["video"]=0 */
    Variable vase = nameToVariableMap["vase"];
    Expression tvPlusVideo =
        nameToVariableMap["tv"] + nameToVariableMap["video"];
    prob.addConstraint(vase.ifThen(tvPlusVideo <= 0.0));

    // x["vase"]=0 -> x["tv"]+x["video"]=2
    // Instead of `vase.ifNotThen()`, we can use this alternative way to create
    // the implication constraint
    Inequality enablePairTwo = prob.addConstraint(tvPlusVideo >= 2.0);
    prob.setIndicator(vase, false, enablePairTwo);

    // By inspecting the problem, we can see the two methods of adding
    // implication constraints indeed yield similar constraint
    prob.writeProb("BinBurglar.lp");

    // Optimize again
    prob.optimize();

    // Check the solution status again
    if (prob.attributes.getSolStatus() != xpress::SolStatus::Optimal) {
      std::ostringstream oss;
      oss << prob.attributes
                 .getSolStatus(); // Convert xpress::SolStatus to String
      throw std::runtime_error("Problem not solved to optimality: " +
                               oss.str());
    }

    // Print out the new solution
    std::cout << std::endl << "*** Solution ***" << std::endl;
    std::cout << "Objective: " << prob.attributes.getObjVal() << std::endl;
    sol = prob.getSolution();
    for (Variable v : x)
      std::cout << v.getName() << " = " << v.getValue(sol) << std::endl;
    std::cout << std::endl;
    return 0;
  } catch (std::exception &e) {
    std::cout << "Exception: " << e.what() << std::endl;
    return -1;
  }
}

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