Loading...
Searching...
No Matches
Variables

Describes how optimization variables are handled in idol and how to use them.

Variables constitute the decision-making elements of an optimization problem. They represent the quantities to be determined in order to optimize an objective function, subject to a set of constraints. In idol, variables are represented by the Var class.

Creating Variables

Creating variables can mainly be done in two ways. The first approach uses the Var constructor and added to a model with the Model::add method. The second one relies on the Model::add_var methods.

Using the Variable Constructor

We begin with the first approach, which employs the Var constructor. Although this method is somewhat less direct, it gives greater insight into how optimization objects are managed within idol.

We focus on the constructor given by

Var(Env&, double, double, VarType, double, std::string)

The constructor requires six arguments. The first specifies the optimization environment, which manages the variable's lifetime and versions. The next two arguments define the lower and upper bounds of the variable, which can be set to infinity using Inf. The fourth argument indicates the type of the variable, for example Continuous, Integer, or Binary. The fifth argument provides the linear coefficient of the variable in the objective function, and the sixth argument assigns a name to the variable.

For example, the following code creates a new variable within the environment.

Var x(env, 0, Inf, Continuous, 2, "x");

This variable is continuous, non-negative, and has an objective coefficient of

  1. It is named "x". Importantly, at this stage the variable does not belong to any model. What has been created is referred to as the "default version" of the variable. By default, if this variable is later added to a model, it will have these attributes within that model. For example, the following code demonstrates how to create the variable and add it to a model.
// Create a variable in the environment.
Var x(env, 0, Inf, Continuous, 2, "x");
// Add the variable to a model
model.add(x);

By default, the variable "x" is added to the model as a continuous, non-negative variable with an objective coefficient of 2. Other constructors are also available in the Var class. For example, it is possible to provide a column associated with the variable so that it is automatically incorporated into the constraint matrix. Columns can be constructed using the LinExpr<Ctr> class in a straightforward and intuitive manner. For more details, see the page dedicated to expressions, in the same tutorial. We provide one illustrative example here.

// This function is assumed to return a vector of constraints.
const std::vector<Ctr> ctrs = get_vector_of_ctrs();
// Create the column associated to x.
LinExpr<Ctr> column = -1 * c[0] + 2 * c[1] + 3 * c[2];
// Create a variable in the environment.
Var x(env, 0, Inf, Integer, -1, std::move(column), "x");
// Add the variable to a model.
model.add(x);

Finally, note that it is possible to avoid adding the default version to a model by overriding it as follows.

// Add the variable to a model, overriding the default version.
model.add(x, TempVar(0, Inf, Continuous, 2, LinExpr<Var>()));

Here, we note the use of the TempVar class. This lightweight class is designed to represent a variable that has not yet been created within an environment. It stores all the attributes of the variable to be created, but cannot be used for any other purpose except for holding these attributes and instantiating an actual variable.

Using the Model

The second approach for creating variables is more straightforward, although it is internally equivalent to the method described above. This approach uses the Model::add_var methods of the Model class. The following code snippet illustrates this usage and should be easy to understand.

const auto x = model.add_var(0, Inf, Continuous, 2, "x");

Note that it is not necessary to pass the environment explicitly, because the environment associated with the model is used automatically. In this single call, two operations are performed: first, a default version of the variable is created, and second, the variable is added to the model. Similarly, it is also possible to add a variable together with a specific column in the constraint matrix.

Creating Multiple Variables at Once

In some cases, it is more convenient to create multiple variables at once. This can be accomplished using the Var::make_vector function or the Model::add_vars method. Both functions require an additional parameter specifying the dimension of the variable set. For example, the following illustrates how to create a set of variables indexed by a \( 2\times 3 \) grid.

// Create a (2,3) "vector" of variables.
const auto x = Var::make_vector(env, Dim<2>(2, 3), 0, Inf, Continuous, "x");
// Add all variables
model.add_vector<Var, 2>(x);
// Print the first variable's name.
std::cout << "x_0_0 = " << x[0][0].name() << std::endl;

Note that we use the Dim class to specify the dimensions of the variable set. The Dim class is a template that takes an integer parameter, which indicates the number of indices for the new variable. In this example, we pass 2 to create a two-dimensional index. The size of each dimension is then specified by providing the appropriate arguments to the constructor of Dim, namely 2 and 3.

Alternatively, the same result can be achieved using methods of the Model class. The following snippet illustrates this approach.

const auto x = model.add_vars(Dim<2>(2,3), 0, Inf, Continuous, "x");

Removing Variables

Once a variable has been added to a model, it can also be removed using the Model::remove method. This operation removes the variable from the model and updates all linear and quadratic constraints in which the variable appears. Attempting to remove a variable that does not belong to the model will result in an exception. To check whether a variable is part of a model, the Model::has method can be used; it returns true if and only if the variable is part of the model.

Note that variables involved in SOS-type constraints cannot be removed directly. This restriction is not limiting in practice, as SOS constraints themselves can be removed and added again if needed.

Accessing Variables

Variables possess two immutable attributes: a name, assigned at the time of creation, and a unique id within the environment. Other attributes are model-specific and can be accessed through the model's methods Model::get_var_<Y>, where <Y> denotes the name of the attribute. The following list summarizes the methods available for retrieving information about variables in a model.

Method Description
lb Returns the lower bound of the variable. May lie in [-Inf, Inf].
ub Returns the upper bound of the variable. May lie in ([-Inf, Inf].
obj Returns the objective coefficient in the linear part of the objective.
type Returns the variable type: Continuous, Integer, or Binary.
column Returns the associated column in the constraint matrix.
index Returns the index of the variable (may change if variables are removed).

The following example demonstrates how to print all free variables in a model.

for (const auto& var : model.vars()) {
const double lb = model.get_var_lb(var);
const double ub = model.get_var_ub(var);
if (is_neg_inf(lb) && is_pos_inf(ub)) {
std::cout << var.name() << " is free." << std::endl;
}
}

One final note regarding variable indices. Although indices may change over time, e.g., if variables are removed from a model, they can still be used to access variables via the Model::get_var_by_index method. The following code snippet illustrates an alternative way to iterate over all variables in a model.

for (unsigned int i = 0, n = model.vars().size(); i < n; ++i) {
// Get the variable by index
const auto& var = model.get_var_by_index(i);
// Print out its name
std::cout << var.name() << std::endl;
}

Modifying Variables

Some attributes of a variable can be modified directly through the model's methods Model::set_var_<Y>, where <Y> denotes the attribute to be changed. The list of attributes is exactly those presented in the previous section.

We conclude with an example showing how to change the objective coefficient of a given variable.

model.set_var_obj(x[0], 5); // sets the objective coefficient of x_0 to 5