| BoolVars.cpp |
// (c) 2024-2025 Fair Isaac Corporation
#include <iomanip> // For setting precision when printing doubles to console
#include <stdexcept> // For throwing exceptions
#include <xpress.hpp>
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;
}
}
|
|
| GeneralConstraints.cpp |
// (c) 2024-2024 Fair Isaac Corporation
/**
* A simple example that formulates some constraints on the minimum and absolute
* values of linear combinations of variables.
*/
#include <iostream>
#include <xpress.hpp>
using namespace xpress;
using namespace xpress::objects;
using xpress::objects::utils::sum;
int main() {
std::cout << "Formulating the general constraint example problem"
<< std::endl;
int const R = 3;
XpressProblem prob;
std::vector<Variable> x =
prob.addVariables(R).withName("x_%d").withUB(20).toArray();
Variable y = prob.addVariable("y");
Variable z = prob.addVariable("z");
Expression objective = sum(x);
// We want to formulate abs(x(0)-2*x(1)) <= 10. We need to introduce
// two auxiliary variables for the argument of the abs function
// and then break down the abs expression in multiple steps
Variable diff1 = prob.addVariable("diff1");
Variable absOfDiff1 = prob.addVariable("absOfDiff1");
prob.addConstraint(diff1 == x[0] - 2 * x[1]);
prob.addConstraint(absOfDiff1.absOf(diff1));
prob.addConstraint(absOfDiff1 <= 10); // Could also be a bound
// We link a new variable to the minimum of the x(i) and
// require this variable to be >= 5
// Clearly, this bound constraint could also be achieved by simply setting
// the bounds of each x variable.
Variable minOfX = prob.addVariable("minOfX");
prob.addConstraint(minOfX.minOf(x));
prob.addConstraint(minOfX >= 5);
// We link variable y to the maximum of other variables, expressions, and
// constants
// y = max(x(2), 20, x(0)-z)
Variable diff2 = prob.addVariable("diff2");
prob.addConstraint(diff2 == x[0] - z);
// the below code is equivalent to using the MaxOf function on the resultant y
// prob.addConstraint(y.MaxOf(new Variable[] {x[2], diff2},
// 20).setName("max_constraint"));
prob.addConstraint(y.maxOf(std::vector<Variable>{x[2], diff2},
std::vector<double>{20}, "max_constraint"));
// set objective function with a maximization sense
prob.setObjective(objective, ObjSense::Maximize);
// write the problem in LP format for manual inspection
std::cout << "Writing the problem to 'GeneralConstraints.lp'" << std::endl;
prob.writeProb("GeneralConstraints.lp", "l");
// Solve the problem
std::cout << "Solving the problem" << std::endl;
prob.optimize();
// check the solution status
std::cout << "Problem finished with SolStatus "
<< to_string(prob.attributes.getSolStatus()) << std::endl;
if (prob.attributes.getSolStatus() != SolStatus::Optimal) {
throw std::runtime_error("Problem not solved to optimality");
}
// print the optimal solution of the problem to the console
std::cout << "Solution has objective value (profit) of "
<< prob.attributes.getObjVal() << std::endl;
std::cout << "*** Solution ***" << std::endl;
auto sol = prob.getSolution();
for (int r = 0; r < R; r++) {
if (r)
std::cout << ", ";
std::cout << "x_" << r << " = " << x[r].getValue(sol);
}
std::cout << std::endl;
std::cout << "y = " << y.getValue(sol) << ", z = " << z.getValue(sol)
<< std::endl;
std::cout << "ABS ( x[0] - 2*x[1] ) = " << absOfDiff1.getValue(sol)
<< ", minOfX = " << minOfX.getValue(sol) << std::endl;
return 0;
}
|
|
| PiecewiseLinear.cpp |
// (c) 2024-2024 Fair Isaac Corporation
/**
* An example that demonstrates how to model a piecewise linear cost function. A
* piecewise linear cost function f(x) assigns a different linear cost function
* for different intervals of the domain of its argument x. This situation
* occurs in real-world problems if there are price discounts available starting
* from a certain quantity of sold goods.
*
* - Example discussed in mipformref whitepaper -
*/
#include <iostream>
#include <xpress.hpp>
using namespace xpress;
using namespace xpress::objects;
int main(void) {
std::cout << "Formulating the piecewise linear example problem" << std::endl;
XpressProblem prob;
Variable x = prob.addVariable("x");
Variable fx = prob.addVariable("fx");
Expression objective = fx;
std::vector<double> BREAKPOINT_X{0, 50, 50,
120, 120, 200}; // the breakpoints x values
std::vector<double> BREAKPOINT_Y{0, 50, 75,
180, 240, 400}; // the breakpoints y values
// Create the break points from the x,y data
std::vector<PwlBreakpoint> breakpoints(BREAKPOINT_X.size());
for (unsigned i = 0; i < breakpoints.size(); ++i)
breakpoints[i] = PwlBreakpoint(BREAKPOINT_X[i], BREAKPOINT_Y[i]);
// add the piecewise linear constraints with the breakpoints
prob.addConstraint(fx.pwlOf(x, breakpoints, "pwl_with_breakpoints"));
// ! Add a lower bound on x to get a somewhat more meaningful model
// x >= 150
x.setLB(150);
// set objective function with a minimization sense
prob.setObjective(objective, ObjSense::Minimize);
// write the problem in LP format for manual inspection
std::cout << "Writing the problem to 'PiecewiseLinear.lp'" << std::endl;
prob.writeProb("PiecewiseLinear.lp", "l");
// Solve the problem
std::cout << "Solving the problem" << std::endl;
prob.optimize();
// check the solution status
std::cout << "Problem finished with SolStatus "
<< to_string(prob.attributes.getSolStatus()) << std::endl;
if (prob.attributes.getSolStatus() != SolStatus::Optimal) {
throw std::runtime_error("Problem not solved to optimality");
}
// print the optimal solution of the problem to the console
std::cout << "Solution has objective value (profit) of "
<< prob.attributes.getObjVal() << std::endl;
std::cout << std::endl;
std::cout << "*** Solution ***" << std::endl;
auto sol = prob.getSolution();
std::cout << "x = " << x.getValue(sol) << ", fx = " << fx.getValue(sol)
<< std::endl;
return 0;
}
|
|
| QuadraticProgramming.cpp |
// (c) 2024-2024 Fair Isaac Corporation
/**
* Small Quadratic Programming example.
*
* <pre>
* minimize x1 + x1^2 +2x1x2 +2x2^2 +x4^2
* s.t.
* C1: x1 +2x2 -4x4 >= 0
* C2: 3x1 -2x3 - x4 <= 100
* C3: 10 <= x1 +3x2 +3x3 -2x4 <= 30
* 0 <= x1 <= 20
* 0 <= x2,x3
* x4 free
* </pre>
*/
#include <iostream>
#include <vector>
#include <xpress.hpp>
using namespace xpress;
using namespace xpress::objects;
using xpress::objects::utils::sum;
int main() {
XpressProblem prob;
prob.callbacks.addMessageCallback(XpressProblem::console);
///// VARIABLES
std::vector<Variable> x(4);
x[0] = prob.addVariable(0, 20, ColumnType::Continuous, "x1");
x[1] = prob.addVariable("x2");
x[2] = prob.addVariable("x3");
x[3] = prob.addVariable(XPRS_MINUSINFINITY, XPRS_PLUSINFINITY,
ColumnType::Continuous, "x4");
///// OBJECTIVE
QuadExpression obj = QuadExpression::create();
obj.addTerm(x[0]);
obj.addTerm(x[0] * x[0]);
obj.addTerm(2 * x[0] * x[1]);
obj.addTerm(2.0 * x[1] * x[1]);
obj.addTerm(x[3].square());
prob.setObjective(obj, ObjSense::Minimize);
///// CONSTRAINTS
prob.addConstraint(sum(x[0], 2 * x[1], -4 * x[3]) >= 0);
prob.addConstraint(sum(3 * x[0], -2 * x[2], -x[3]) <= 100);
prob.addConstraint(sum(x[0], 3 * x[1], 3 * x[2], -2 * x[3]).in(10, 30));
///// SOLVING + OUTPUT
prob.writeProb("qp.lp");
prob.optimize();
std::cout << "Problem status: " << to_string(prob.attributes.getLpStatus())
<< std::endl;
if (prob.attributes.getLpStatus() != LPStatus::Optimal)
throw std::runtime_error("optimization failed with status " +
to_string(prob.attributes.getLpStatus()));
std::cout << "Objective function value: " << prob.attributes.getObjVal()
<< std::endl;
auto sol = prob.getSolution();
for (int i = 0; i < 4; i++)
std::cout << x[i].getName() << ": " << x[i].getValue(sol) << ", ";
std::cout << std::endl;
return 0;
}
|
|
| SpecialOrderedSets.cpp |
// (c) 2024-2024 Fair Isaac Corporation
/**
* Approximation of a nonlinear function by a special ordered set (SOS-2). An
* SOS-2 is a constraint that allows at most 2 of its variables to have a
* nonzero value. In addition, these variables have to be adjacent.
*
* - Example discussed in mipformref whitepaper -
*/
#include <iostream>
#include <xpress.hpp>
using namespace xpress;
using namespace xpress::objects;
using xpress::objects::utils::scalarProduct;
using xpress::objects::utils::sum;
int main() {
int NB = 4; // number of breakpoints
std::vector<double> B_X{1, 2.5, 4.5, 6.5}; // X coordinates of breakpoints
std::vector<double> B_Y{1.5, 6, 3.5, 2.5}; // Y coordinates of breakpoints
std::cout << "Formulating the special ordered sets example problem"
<< std::endl;
XpressProblem prob;
// create one w variable for each breakpoint. We express
auto w = prob.addVariables(NB).withName("w_%d").withUB(1).toArray();
Variable x = prob.addVariable("x");
Variable y = prob.addVariable("y");
// Define the SOS-2 with weights from B_X. This is necessary
// to establish the ordering between the w variables.
prob.addConstraint(SOS::sos2(w, B_X, "SOS_2"));
// We use the w variables to express a convex combination.
// In combination with the above SOS-2 condition,
// X and Y are represented as a convex combination of 2 adjacent
// breakpoints.
prob.addConstraint(sum(w) == 1);
// The below constraints express the actual locations of X and Y
// in the plane as a convex combination of the breakpoints, subject
// to the assignment found for the w variables.
prob.addConstraint(x == scalarProduct(w, B_X));
prob.addConstraint(y == scalarProduct(w, B_Y));
// set lower and upper bounds on x
x.setLB(2);
x.setUB(6);
// set objective function with a minimization sense
prob.setObjective(y, ObjSense::Minimize);
// write the problem in LP format for manual inspection
std::cout << "Writing the problem to 'SpecialOrderedSets.lp'" << std::endl;
prob.writeProb("SpecialOrderedSets.lp", "l");
// Solve the problem
std::cout << "Solving the problem" << std::endl;
prob.optimize();
// check the solution status
std::cout << "Problem finished with SolStatus "
<< to_string(prob.attributes.getSolStatus()) << std::endl;
if (prob.attributes.getSolStatus() != SolStatus::Optimal) {
throw std::runtime_error("Problem not solved to optimality");
}
// print the optimal solution of the problem to the console
std::cout << "Solution has objective value (profit) of "
<< prob.attributes.getObjVal() << std::endl;
std::cout << "*** Solution ***" << std::endl;
auto sol = prob.getSolution();
for (int b = 0; b < NB; b++) {
std::cout << "w_" << b << " = " << w[b].getValue(sol);
if (b < NB - 1)
std::cout << ", ";
else
std::cout << std::endl;
}
std::cout << "x = " << x.getValue(sol) << ", y = " << y.getValue(sol)
<< std::endl;
return 0;
}
|
|
| SpecialOrderedSetsQuadratic.cpp |
// (c) 2024-2024 Fair Isaac Corporation
/**
* Approximation of a quadratic function in 2 variables by special ordered sets
* (SOS-2). An SOS-2 is a constraint that allows at most 2 of its variables to
* have a nonzero value. In addition, these variables have to be adjacent.
*
* - Example discussed in mipformref whitepaper -
*/
#include <iostream>
#include <xpress.hpp>
using namespace xpress;
using namespace xpress::objects;
using xpress::objects::utils::scalarProduct;
using xpress::objects::utils::sum;
int main() {
int const NX = 10; // number of breakpoints on the X-axis
int const NY = 10; // number of breakpoints on the Y-axis
std::vector<double> X(NX); // X coordinates of grid points
std::vector<double> Y(NY); // Y coordinates of breakpoints
// two dimensional array of function values on the grid points
std::vector<std::vector<double>> F_XY(NX, std::vector<double>(NY));
// assign the toy data
for (int i = 0; i < NX; i++)
X[i] = i + 1;
for (int i = 0; i < NY; i++)
Y[i] = i + 1;
for (int i = 0; i < NX; i++)
for (int j = 0; j < NY; j++)
F_XY[i][j] = (X[i] - 5) * (Y[j] - 5);
std::cout << "Formulating the special ordered sets quadratic example problem"
<< std::endl;
XpressProblem prob;
// create one w variable for each X breakpoint. We express
auto wx = prob.addVariables(NX)
.withName("wx_%d")
// this upper bound i redundant because of the convex
// combination constraint on the sum of the wx
.withUB(1)
.toArray();
// create one w variable for each Y breakpoint. We express
auto wy = prob.addVariables(NY)
.withName("wy_%d")
// this upper bound i redundant because of the convex
// combination constraint on the sum of the wy
.withUB(1)
.toArray();
// create a two-dimensional array of w variable for each grid point. We
// express
auto wxy = prob.addVariables(NX, NY)
.withName("wxy_%d_%d")
// this upper bound is redundant because of the convex
// combination constraint on the sum of the wy
.withUB(1)
.toArray();
Variable x = prob.addVariable("x");
Variable y = prob.addVariable("y");
Variable fxy = prob.addVariable("fxy");
// make fxy a free variable
fxy.setLB(XPRS_MINUSINFINITY);
// Define the SOS-2 constraints with weights from X and Y.
// This is necessary to establish the ordering between
// variables in wx and in wy.
prob.addConstraint(SOS::sos2(wx, X, "SOS_2_X"));
prob.addConstraint(SOS::sos2(wy, Y, "SOS_2_Y"));
prob.addConstraint(sum(wx) == 1);
prob.addConstraint(sum(wy) == 1);
// link the wxy variables to their 1-dimensional colleagues
prob.addConstraints(NX, [&](auto i) {
return wx[i] == sum(NY, [&](auto j) { return wxy[i][j]; });
});
prob.addConstraints(NY, [&](auto j) {
return wy[j] == sum(NX, [&](auto i) { return wxy[i][j]; });
});
// now express the actual x, y, and f(x,y) coordinates
prob.addConstraint(x == scalarProduct(wx, X));
prob.addConstraint(y == scalarProduct(wy, Y));
prob.addConstraint(fxy == sum(NX, [&](auto i) {
return sum(
NY, [&](auto j) { return wxy[i][j] * F_XY[i][j]; });
}));
// set lower and upper bounds on x and y
x.setLB(2);
x.setUB(10);
y.setLB(2);
y.setUB(10);
// set objective function with a minimization sense
prob.setObjective(fxy, ObjSense::Minimize);
// write the problem in LP format for manual inspection
std::cout << "Writing the problem to 'SpecialOrderedSetsQuadratic.lp'"
<< std::endl;
prob.writeProb("SpecialOrderedSetsQuadratic.lp", "l");
// Solve the problem
std::cout << "Solving the problem" << std::endl;
prob.optimize();
// check the solution status
std::cout << "Problem finished with SolStatus "
<< to_string(prob.attributes.getSolStatus()) << std::endl;
if (prob.attributes.getSolStatus() != SolStatus::Optimal) {
throw std::runtime_error("Problem not solved to optimality");
}
// print the optimal solution of the problem to the console
std::cout << "Solution has objective value (profit) of "
<< prob.attributes.getObjVal() << std::endl;
std::cout << "*** Solution ***" << std::endl;
auto sol = prob.getSolution();
for (int i = 0; i < NX; i++) {
std::cout << "wx_" << i << " = " << wx[i].getValue(sol);
if (i < NX - 1)
std::cout << ", ";
else
std::cout << std::endl;
}
for (int j = 0; j < NY; j++) {
std::cout << "wy_" << j << " = " << wy[j].getValue(sol);
if (j < NX - 1)
std::cout << ", ";
else
std::cout << std::endl;
}
std::cout << "x = " << x.getValue(sol) << ", y = " << y.getValue(sol)
<< std::endl;
return 0;
}
|
|