NO FRAMES

Concepts
Arrays
Assert and NDEBUG
Column-Wise Modeling
Exceptions, Errors
Extraction
Handle Class
Lazy Copy
Normalization: Reducing Linear Terms
Notification
Deletion of Extractables
Obsolete Functions & Classes
Arrays

For most basic classes (such as IloNumVar or IloConstraint) in Concert Technology, there is also a corresponding class of arrays where the elements of the array are instances of that basic class. For example, elements of an instance of IloConstraintArray are instances of the class IloConstraint.

Arrays in an Environment

Every array must belong to an environment (an instance of IloEnv). In other words, when you create a Concert Technology array, you pass an instance of IloEnv as a parameter to the constructor. All the elements of a given array must belong to the same environment.

Extensible Arrays

Concert Technology arrays are extensible. That is, you can add elements to the array dynamically. You add elements by means of the add member function of the array class.

You can also remove elements from an array by means of its remove member function.

References to an array change whenever an element is added to or removed from the array.

Arrays as Handles

Like other Concert Technology objects, arrays are implemented by means of two classes: a handle class corresponding to an implementation class. An object of the handle class contains a data member (the handle pointer) that points to an object (its implementation object) of the corresponding implementation class. As a Concert Technology user, you will be working primarily with handles.

Copying Arrays

Many handles may point to the same implementation object. This principle holds true for arrays as well as other handle classes in Concert Technology. When you want to create more than one handle for the same implementation object, you should use either the copy constructor or the assignment operator of the array class. For example,

  
              IloNumArray array(env);     // creates a handle pointing to new impl  
              IloNumArray array1(array);  // creates a handle pointing to same impl  
              IloNumArray array2;         // creates an empty handle  
              array2 = array;        // sets impl of handle array2 to impl of array
Programming Hint: Using Arrays Efficiently

If your application only reads an array (that is, if your function does not modify an element of the array), then we recommend that you pass the array to your function as a const parameter. This practice forces Concert Technology to access the const conversion of the index operator (that is, operator[]), which is faster.

Assert and NDEBUG

Most member functions of classes in Concert Technology are inline functions that contain an assert statement. This statement asserts that the invoking object and the member function parameters are consistent; in some member functions, the assert statement checks that the handle pointer is non-null. These statements can be suppressed by the macro NDEBUG. This option usually reduces execution time. The price you pay for this choice is that attempts to access through null pointers are not trapped and usually result in memory faults.

Compilation with assert statements will not prevent core dumps by incorrect code. Instead, compilation with assert statements moves the execution of the incorrect code (the core dump, for example) to a place where you can see what is causing the problem in a source code debugger. Correctly written code will never cause one of these Concert Technology assert statements to fail.

Column-Wise Modeling

Concert Technology supports column-wise modeling, a technique widely used in the math programming and operations research communities to build a model column by column. In Concert Technology, creating a new column is comparable to creating a new variable and adding it to a set of constraints. You use an instance of IloNumColumn to do so. An instance of IloNumColumn allows you to specify to which constraints or other extractable objects Concert Technology should add the new variable along with its data. For example, in a linear programming problem (an LP), if the new variable will appear in some linear constraints as ranges (instances of IloRange), you need to specify the list of such constraints along with the non zero coefficients (a value of IloNum) for each of them.

You then create a new column in your model when you create a new variable with an instance of IloNumColumn as its parameter. When you create the new variable, Concert Technology will add it along with appropriate parameters to all the extractable objects you have specified in the instance of IloNumColumn.

Instead of building an instance of IloNumColumn, as an alternative, you can use a column expression directly in the constructor of the variable. You can also use instances of IloNumColumn within column expressions.

The following undocumented classes provide the underlying mechanism for column-wise modeling:

  • IloAddValueToObj
  • IloAddValueToRange

The following operators are useful in column-wise modeling:

IloAddValueToRange operator() (IloNum value);

IloAddValueToObj operator () (IloNum value);

That is, the operator () in extractable classes, such as IloRange or IloObjective, creates descriptors of how Concert Technology should add the new, yet-to-be-created variable to the invoking extractable object.

You can use the operator + to link together the objects returned by operator () to form a column. You can then use an instance of IloNumColumn to build up column expressions within a programming loop and thus save them for later use or to pass them to functions.

Here is how to use an instance of IloNumColumn with operators from IloRange and IloObjective to create a column with a coefficient of 2 in the objective, with 10 in range1, and with 3 in range2. The example then uses that column when it creates the new variable newvar1, and it uses column expressions when it creates newvar2 and newvar3.

    IloNumColumn col = obj(2) + range1(10) + range2(3);
    IloNumVar newvar1(col);
    IloNumVar newvar2(col + range3(17));
    IloNumVar newvar3(range1(1) + range3(3));
    

In other words, given an instance obj of IloObjective and the instances range1, range2, and range3 of IloRange, those lines create the new variables newvar1, newvar2, and newvar3 and add them as linear terms to obj, range1, and range3 in the following way:

    obj: + 2*newvar1 + 2*newvar2
    range1: +10*newvar1 + 10*newvar2 + 1*newvar3
    range2: + 3*newvar1 + 3*newvar2
    range3: + 17*newvar2 +3*newvar3
    

For more information, refer to the documentation of IloNumColumn,IloObjective, and IloRange.

Exceptions, Errors

An exception is thrown; it is not allocated in a Concert Technology environment; it is not allocated on the C++ heap. It is not necessary for you as a programmer to delete an exception explicitly. Instead, the system calls the constructor of the exception to create it, and the system calls the destructor of the exception to delete it.

When exceptions are enabled on a platform that supports C++ exceptions, an instance of a class of Concert Technology is able to throw an exception in case of error. On platforms that do not support C++ exceptions, it is possible for Concert Technology to exit in case of error.

Programming Hint: Throwing and Catching Exceptions

Exceptions are thrown by value. They are not allocated on the C++ heap, nor in a Concert Technology environment. The correct way to catch an exception is to catch a reference to the error (indicated by the ampersand &), like this:

catch(IloException& oops);

Extraction

Concert Technology offers classes for you to design a model of your problem. You can then invoke an algorithm to extract information from your model to solve the problem. In this context, an algorithm is an instance of a class such as IloCplex, documented in the ILOG CPLEX Reference Manual, or IloSolver, documented in the ILOG Solver Reference Manual.

For details about what each algorithm extracts from a model, see the reference manual documenting that algorithm. For example, the ILOG CPLEX Reference Manual lists precisely which classes of Concert Technology are extracted by an instance of IloCplex. In general terms, an instance of IloCplex extracts a model as rows and columns, where the columns indicate decision variables of the model. Also in general terms, an instance of IloSolver extracts an instance of a class whose name begins Ilo to a corresponding instance of a class whose name begins Ilc. For example, an instance of IloAllDiff is extracted by IloSolver as an instance of IlcAllDiff.

Handle Class

Most Concert Technology entities are implemented by means of two classes: a handle class and an implementation class, where an object of the handle class contains a data member (the handle pointer) that points to an object (its implementation object) of the corresponding implementation class. As a Concert Technology user, you will be working primarily with handles.

As handles, these objects should be passed in either of these ways:

  • as const by value (when no change is involved);
  • by reference (when the function to which the handle is passed changes the implementation of that handle).

They should be created as automatic objects, where "automatic" has the usual C++ meaning.

Member functions of a handle class correspond to member functions of the same name in the implementation class.

Lazy Copy

Concert Technology makes a lazy copy when you use any of the following objects inside a predefined Concert Technology object:

  • an expression (an instance of IloExpr or one of its subclasses),
  • an array (such as an instance of IloNumArray),
  • a column (an instance of IloNumColumn),
  • or a set (such as an instance of IloIntSet).

That is, a physical copy of those objects is created only when needed.

In Concert Technology, expressions, arrays, columns, and sets are implemented by handle classes and corresponding implementation classes. One or more handles may point to the same implementation object. For example, many instances of the handle class IloNumVarArray may point to the same implementation object.

A handle may be empty; that is, it may point to 0 (zero). You can test whether a handle is empty by means of the member function handle.getImpl. If that member function returns 0, the handle points to a null implementation.

When you modify an expression, an array, a column, or a set that has been used in a Concert Technology object, Concert Technology considers whether the handle you are modifying is the sole reference to the corresponding implementation object. If so, Concert Technology simply makes the modification.

In contrast, if the handle you are modifying points to an implementation object that is used by other objects predefined in Concert Technology, Concert Technology first copies the implementation object for the handle you are modifying and then makes the modification. The other handles pointing to the original implementation object remain unchanged and your modification has no impact on them.

Examples:

Here is an example illustrating lazy copy of variables:

  IloNumVar a1(env, 0, 10);
  IloNumVar a2(env, 0, 10);
  IloNumVar a3(env, 0, 0);
  IloNumVarArray A(env, 2, a1, a2);
  IloConstraint ct = IloAllDiff(env, A);
  A.add (a3);
  

Because of the lazy copy, even though a3 was added to A, ct uses only a1 and a2.

Normalization: Reducing Linear Terms

Normalizing is sometimes known as reducing the terms of a linear expression.

Linear expressions consist of terms made up of constants and variables related by arithmetic operations; for example, x + 3y is a linear expression of two terms consisting of two variables. In some expressions, a given variable may appear in more than one term, for example, x + 3y +2x. Concert Technology has more than one way of dealing with linear expressions in this respect, and you control which way Concert Technology treats expressions from your application.

In one mode, Concert Technology analyzes linear expressions that your application passes it and attempts to reduce them so that a given variable appears in only one term in the linear expression. This is the default mode. You set this mode with the member function IloEnv::setNormalizer(IloTrue).

In the other mode, Concert Technology assumes that no variable appears in more than one term in any of the linear expressions that your application passes to Concert Technology. We call this mode assume normalized linear expressions. You set this mode with the member function IloEnv::setNormalizer(IloFalse).

In classes such as IloExpr or IloRange, there are member functions that check the setting of the member function IloEnv::setNormalizer in the environment and behave accordingly. The documentation of those member functions indicates how they behave with respect to normalization.

When you set IloEnv::setNormalizer(IloFalse), those member functions assume that no variable appears in more than one term in a linear expression. This mode may save time during computation, but it entails the risk that a linear expression may contain one or more variables, each of which appears in one or more terms. Such a case may cause certain assertions in member functions of a class to fail if you do not compile with the flag -DNDEBUG.

By default, those member functions attempt to reduce expressions. This mode may require more time during preliminary computation, but it avoids of the possibility of a failed assertion in case of duplicates.

For more information, refer to the documentation of IloEnv,IloExpr, and IloRange.

Notification

You may modify the elements of a model in Concert Technology. For example, you may add or remove constraints, change the objective, add or remove columns, add or remove rows, and so forth.

In order to maintain consistency between a model and the algorithms that may use it, Concert Technology notifies algorithms about changes in the objects that the algorithms have extracted. In this manual, member functions that are part of this notification system are indicated like this:

Note
This member function notifies Concert Technology algorithms about this change of this invoking object.
Deletion of Extractables

As a modeling layer, Concert allows the creation and destruction of extractables. This is accessible through the method IloExtractable::end() and IloExtractableArray::endElements() method. The goal of these methods is to reclaim memory associated with the deleted objects while maintaining the safest possible Concert environment. In this context, a safe Concert environment is defined by the property that no object points to a deleted object; this is referred to as a dangling pointer in C++.

There exist two paradigms to ensure the safeness of the delete operation. The first, linear mode, comes from math programming and is possible only on extractables and objects used in linear programming. The second, safe generic mode, is more strict and is valid on all Concert extractables.

You can access both paradigms by calling IloEnv::setDeleter(IloDeleterMode mode), where mode may be IloLinearDeleterMode or IloSafeDeleterMode.

Linear Mode

To use linear mode, you must either

In linear mode, the following behavior is implemented:

  • If a range constraint is deleted, it is removed from the models that contain it.
  • If a variable is deleted, its coefficient is set to 0 in the ranges, expressions, and objectives where it appears. The variable is removed from the SOS1, SOS2, and IloConversion where it appears.

Example

This example tests the linear mode deletion of a variable x.

  void TestLinearDeleter() {
    IloEnv env;
    env.out() << "TestLinearDeleter" << endl;
    try {
      IloModel model(env);
      IloNumVar x(env, 0, 10, "x");
      IloNumVar y(env, 0, 10, "y");
      IloConstraint con = (x + y <= 0);
      IloConstraint con2 = y >= 6;
      IloNumVarArray ar(env, 2, x, y);
      IloSOS1 sos(env, ar, "sos");
      model.add(con);
      model.add(con2);
      model.add(sos);
      env.out() << "Before Delete" << endl;
      env.out() << model << endl;
      x.end();
      con2.end();
      env.out() << "After Delete" << endl;
      env.out() << model << endl;    
    } catch (IloException& e) {
      cout << "Error : " << e << endl;
    }
    env.end();
  }
  

The example produces the following output:

  TestLinearDeleter
  Before Delete
  IloModel model0 = {
  IloRange rng3(    1 * x + 1 * y ) <= 0
  
  IloRange rng46 <=(    1 * y )
  
  IloSOS1I (sos)
    _varArray [x(F)[0..10], y(F)[0..10]]
    _valArray []
      
  }
  
  After Delete  
  IloModel model0 = {
  IloRange rng3(    1 * y ) <= 0
  
  IloSOS1I (sos)
    _varArray [y(F)[0..10]]
    _valArray []
  }

Safe Generic Mode

To use safe generic mode, you must:

  • call IloEnv::setDeleter(IloSafeDeleterMode), and
  • add #include <ilconcert/ilodeleter.h> to your program.

In this mode, the environment builds a dependency graph between all extractables. This graph contains all extractables created

Objects not managed by this dependency graph are referred to here as "nondeletable". An attempt to delete a nondeletable object will throw an exception.

We recommended that you create this graph just after the creation of the environment and that you refrain from using IloEnv::unsetDeleter. We make these recommendations because building an incomplete dependency graph is very error prone and should only be attempted by advanced users. A good example of this incomplete graph is the separation of a model between a nondeletable base model and deletable extensions of this base model.

Calling IloExtractable::end() on extractable xi will succeed only if no other extractable uses extractable xi. If this is not the case, a call to IloExtractable::end() will throw an exception IloDeleter::RequiresAnotherDeletionException indicating which extractable uses the extractable that you want to delete.

Example

This example shows an attempt to delete one extractable that is used by another.

  
  void TestSafeDeleter() {
    IloEnv env;
    env.out() << "TestSafeDeleter" << endl;
    env.setDeleter(IloSafeDeleterMode);
    try {
      IloModel model(env);
      IloNumVar x(env, 0, 10);
      IloNumVar y(env, 0, 10);
      IloConstraint con = (x + y <= 0);
      try {
        x.end();
      } catch (IloDeleter::RequiresAnotherDeletionException &e) {
        cout << "Caught " << e << endl;
        e.getUsers()[0].end();
        e.end();
      }
      x.end();
    } catch (IloException& e) {
      cout << "Error : " << e << endl;
    }
    env.unsetDeleter();
    env.end();
  }
  

The example produces the following output:

  TestSafeDeleter
  Caught You cannot end x1(F)[0..10] before IloRange rng3(  1 * x1 + 1 * x2  ) <= 0
  

To address this, you should use the IloExtractableArray::endElements() method. With this method, all extractables appearing in the array are deleted one after another. Thus, if an extractable is used by another extractable and this other extractable is deleted before the first one, the system will not complain and will not throw an exception.

Example

This example illustrates the use of the endElements() method

  
  void TestSafeDeleterWithArray() {
    IloEnv env;
    env.out() << "TestSafeDeleterWithArray" << endl;
    env.setDeleter(IloSafeDeleterMode);
    try {
      IloModel model(env);
      IloNumVar x(env, 0, 10);
      IloNumVar y(env, 0, 10);
      IloConstraint con = (x + y <= 0);
      IloExtractableArray ar(env, 2, con, x);
      ar.endElements();
    } catch (IloException& e) {
      cout << "Error : " << e << endl;
    }
    env.unsetDeleter();
    env.end();
  }
  

The example will not throw an exception.

Note
Please note that in this last example, the constraint con must appear before the variable x as it will be deleted before the variable x.
Obsolete Functions & Classes
Obsolete Function or ClassReplaced By
IloNumSetIloIntSet
IloNumSetVarIloIntSetVar
IloNumSetVarArrayIloIntSetVarArray
IloExprBaseIloNumExprArg and IloIntExprArg
IloExprNodeIloNumExprArg and IloIntExprArg
IloExprArgIloNumExprArg and IloIntExprArg
IloExprIIloNumLinTermI (undocumented)