Loading...
Searching...
No Matches
Basics

Describes the basic usage of idol and shows how to model a MILP and solve it.

A Toy Example

To illustrate the modeling capabilities of idol, we begin with a small example given by the MILP

\[\begin{aligned} \min_{x,y} \quad & -x - 2y \\ \text{s.t.} \quad & -x + y \le 1, \\ & 2x + 3y \le 12, \\ & 3x + 2y \le 12, \\ & x,y\in\mathbb{Z}_{\ge 0}. \end{aligned} \]

This is a minimization problem which involves two integer variables, \( x \) and \( y \), both constrained to be non-negative. The feasible region is defined by three linear inequalities.

The continuous relaxation of this problem (where \( x \) and \( y \) are allowed to take real values) has a unique solution at \( (x^*, y^*) = (2.4, 2.4) \), with objective value \( -7.2 \). The original integer-constrained problem also admits a unique solution, which is attained at \( (x^*, y^*) = (2, 2)\), with objective value \( -5 \).

Modeling this problem in idol is straightforward and will look familiar if you use other optimization frameworks such as JuMP in Julia or the Gurobi C++, or even Python, interface.

The following code snippet is largely self-explanatory.

#include <iostream>
#include "idol/modeling.h"
using namespace idol;
int main(int t_argc, const char** t_argv) {
Env env;
// Create a new model.
Model model(env);
// Create decision variables x and y.
const auto x = model.add_var(0, Inf, Integer, -1, "x");
const auto y = model.add_var(0, Inf, Integer, -2, "y");
// Create constraints.
const auto c1 = model.add_ctr(-x + y <= 1);
const auto c2 = model.add_ctr(2 * x + 3 * y <= 12);
const auto c3 = model.add_ctr(3 * x + 2 * y <= 12);
return 0;
}

Let's walk through this code. First, we create a new optimization environment that will store all our optimization objects such as variables or constraints. This is done with the Env class. Destroying an environment automatically destroys all objects which were created with this environment.

Then, we create an optimization model using the Model class. In idol, all models are for minimization problems. Decision variables are created in using the Model::add_var method. There, we set the lower bound to 0 and an infinite upper bound using the defined constant Inf. Both variables are defined as Integer. Note that other types are possible, e.g., Continuous and Binary. The objective coefficients are also set in these lines by the fourth argument (note that this can also be done afterward). The last argument corresponds to the internal name of that variable and is mainly used for debugging and printing.

Finally, we add constraints to the model to define the feasible region of the problem. To do this, we use the Model::add_ctr method.

Note that at this stage we are only formulating the problem; no optimization is being performed yet. Solving the model is the focus of the section.

Using an External Solver

To solve a model, we need to attach an optimizer to it. This is done using the Model::use method that takes a single argument. This argument is what is called an optimizer factory, i.e., an object that will create on demand an actual optimizer for solving the model.

Here, we continue ongoing example and illustrate how to use the commercial solver Gurobi to compute a solution to this problem. To do so, we use the Gurobi class, which is an optimizer factory for the optimizer Optimizers::Gurobi that interfaces with the Gurobi C API.

Here is a code snippet.

model.use(Gurobi());
model.optimize();
const auto status = model.get_status();
std::cout << "Status: " << status << std::endl;
std::cout << "Reason: " << model.get_reason() << std::endl;
if (status == Optimal || status == Feasible) {
std::cout << "Best Obj. " << model.get_best_obj() << std::endl;
std::cout << "x = " << model.get_var_primal(x) << std::endl;
std::cout << "y = " << model.get_var_primal(y) << std::endl;
}

First, we tell idol to "use" the Gurobi optimizer factory for this model. Then, we call the Model::optimize method that, indeed, creates and calls an optimizer to solve the problem. Finally, we retrieve some basic information regarding the solution of our problem. If the status is Optimal or Feasible, we print out the primal values.

Obviously, Gurobi must be installed on your machine for this code to successfully run. Note that Gurobi is dynamically loaded by idol provided that your GUROBI_HOME environment variable is correctly set up.

Note that there are other optimizers, for instance, here is how to use HiGHS instead.

model.use(HiGHS());

More Advanced Topics

The previous sections should be enough for you to get started with the library.

Next is a list of more in-depth pages about basic concepts in idol.