FICO
FICO Xpress Optimization Examples Repository
FICO Optimization Community FICO Xpress Optimization Home
Back to examples browserPrevious exampleNext example

Constraint types - Logical, general, SOS, quadratic

Description
Small examples showing how to define special constraint types:
  • Stating logic clauses that resemble SAT-type formulations (BoolVars).

ctrtypes_cpp.zip[download all files]

Source Files
By clicking on a file name, a preview is opened at the bottom of this page.
BoolVars.cpp[download]





BoolVars.cpp

#include <xpress.hpp>
#include <iomanip>   // For setting precision when printing doubles to console
#include <stdexcept> // For throwing exceptions

using namespace xpress;
using namespace xpress::objects;

/* Problem showing the use of binary variables and how to model
constraints such as OR, AND using 'resultant variables'
*/
int main() {
    try {
        // Number of variables to create
        int R = 5;

        // Create a problem instance
        XpressProblem prob;
        // prob.callbacks.addMessageCallback(XpressProblem::console);

        // Create boolean variables x
        std::vector<xpress::objects::Variable> x = prob.addVariables(R)
            .withType(ColumnType::Binary)
            .withName("x_%d")
            .toArray();

        // Create boolean variables xNeg (intended as xNeg = 1-x, but the problem does not know this yet)
        auto xNeg = prob.addVariables(R)
            .withType(ColumnType::Binary)
            .withName("xNeg_%d")
            .toArray();

        // Now add the relation that x[r] + xNeg[r] = 1 for all r in [0...R)
        prob.addConstraints(R, [&](int r){ return x[r] + xNeg[r] == 1.0; });

        // Add some random constraints
        prob.addConstraint(x[2].eq(xNeg[3])); // Add constraint x[2] == xNeg[3]

        // Now we are going to construct constraints using OR and AND operators on the variables
        // We need a 'resultant variable' to construct these OR and AND constraints
        // We create two here, with names 'TRUE' and 'FALSE'
        Variable trueVar = prob.addVariable(ColumnType::Binary, "TRUE");
        Variable falseVar = prob.addVariable(ColumnType::Binary, "FALSE");

        // Fix these helper variables to 1 (true) and 0 (false), respectively, (hence the naming of these 'variables')
        trueVar.fix(1);  // Add constraint trueVar  == 1
        falseVar.fix(0); // Add constraint falseVar == 0

        // We can now use trueVar as placeholder for '== 1':
        prob.addConstraint(trueVar.andOf(x[0], xNeg[4]));  // Add constraint trueVar == AND(x[0], xNeg[4])
        prob.addConstraint(trueVar.orOf(x[0], x[2]));      // Add constraint trueVar == OR(x[0], x[2])

        // For more complicated expressions, we need non-fixed resultant variables for each operator
        Variable andResultant1 = prob.addVariable(ColumnType::Binary, "andresultant1");
        Variable andResultant2 = prob.addVariable(ColumnType::Binary, "andresultant2");

        // Add constraint that AND(x[0] + x[1] + x[2]) == andResultant1
        std::vector<Variable> subrange1(x.begin(), x.begin() + 3); // We first explicitly create a subarray of variables
        prob.addConstraint(andResultant1.andOf(subrange1));

        // Add constraint that AND(xNeg[3] + xNeg[4]) == andResultant2. Now we create the subarray within the function call
        prob.addConstraint(andResultant2.andOf( std::vector<Variable>(xNeg.begin()+3, xNeg.end()) ));

        // Now finally create constraint definition that OR(andResultant1, andResultant2) == trueVar (which equals 1)
        GeneralConstraintDefinition new_constraint_def = trueVar.orOf(andResultant1, andResultant2);
        // Now actually add the GeneralConstraintDefinition to the problem to get a GeneralConstraint
        GeneralConstraint new_constraint = prob.addConstraint(new_constraint_def);

        // Finally, add a constraint that none of xNeg[0...2] should be true
        prob.addConstraint(falseVar.orOf( std::vector<Variable>(xNeg.begin(), xNeg.begin() + 3) ));

        // write the problem in LP format for manual inspection
        std::cout << "Writing the problem to 'BoolVars.lp'" << std::endl;
        prob.writeProb("BoolVars.lp", "l");

        // Solve the problem
        std::cout << "Solving the problem" << std::endl;
        prob.optimize();

        // Check the solution status
        std::cout << "Problem finished with SolStatus " << prob.attributes.getSolStatus() << std::endl;
        if (prob.attributes.getSolStatus() != xpress::SolStatus::Optimal) {
            throw std::runtime_error("Problem not solved to optimality");
        }

        // Print the solution to console (first set precision to e.g. 5)
        std::cout << std::fixed << std::setprecision(5);
        std::cout << "Solution has objective value (profit) of " << prob.attributes.getObjVal() << std::endl;
        std::cout << std::endl << "*** Solution ***" << std::endl;

        // Retrieve the solution values in one go
        std::vector<double> sol = prob.getSolution();

        // Loop over the relevant variables and print their name and value
        for (Variable x_r : x) std::cout << x_r.getName() << " = " << x_r.getValue(sol) << std::endl;
        std::cout << std::endl;

        for (Variable xNeg_r : xNeg) std::cout << xNeg_r.getName() << " = " << xNeg_r.getValue(sol) << std::endl;
        std::cout << std::endl;
        return 0;
    }
    catch (std::exception& e) {
        std::cout << "Exception: " << e.what() << std::endl;
        return -1;
    }
}

Back to examples browserPrevious exampleNext example