Initializing help system before first use

Constraint types - Logical, general, SOS, quadratic


Type: Programming
Rating: 2 (easy-medium)
Description: Small examples showing how to define special constraint types:
  • Stating logic clauses that resemble SAT-type formulations (BoolVars).
File(s): BoolVars.cpp


BoolVars.cpp
// (c) 2024-2025 Fair Isaac Corporation
#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;
    }
}

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