| |||||||||||
Burglar - Formulating logical constraints 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.
Source Files By clicking on a file name, a preview is opened at the bottom of this page.
BinBurglar.cpp #include <xpress.hpp> #include <stdexcept> // For throwing exceptions 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(static_cast<int>(values.size())) .withType(ColumnType::Binary) .withName([&](int 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; } } | |||||||||||
© Copyright 2024 Fair Isaac Corporation. |