FRAMES NO FRAMES

Selectors
PREVIOUS NEXT

Selectors are objects which select one object from a container of objects based on some selection criteria. In particular, selectors are used in Solver search goals to select the decision that will be taken at a search node. For example, a selector could be used to select which variable to explore or to select which value to assign to a given variable at a search node.

Selectors of objects of class Object from a container Container are instances of the template class IloSelector<Object,Container>.

For example, selectors of integer variables from an integer variable array are instances of IloSelector<IlcIntVar,IlcIntVarArray>.

A selector implements a function IloBool IloSelector<Object,Container>::select(Object& selected, Container c) that returns IloFalse if and only if no object could be selected and, otherwise, binds the variable selected given as the first argument to the selected object.

When the selector is used by a search goal, the member function select is called during the execution of the goal to perform the selection of the next decision.

Selectors can either be created on a Concert environment (IloEnv) or on the reversible heap of a solver (IloSolver) depending on the signature that is used to construct the selector.

There are two ways to define a selector:

When it is possible, IBM advises you to use the second approach as it is more flexible and modular.

The first way to define a selector is to use the ILOSELECTORi macros to define the selection code of a selector. Within the code of the macro, the user specifies which element Object of the given container of type Container is selected by invoking the select(Object) method.

For example, you could define a selector of integer variables from an integer variable array that selects a random variable in the array using the following code. This selector has one data field that stores a random generator.

ILOSELECTOR1(RandomVariableSelector,
             IlcIntVar,
             IlcIntVarArray, array,
             IloRandom, random) {
   select(array[random.getInt(array.getSize()-1)]); // Selected object
}

This macro defines the following two functions, which return a selector allocated either on a Concert environment (IloEnv) or on the reversible heap of a solver (IloSolver).

IloEnv env;
IloSolver solver = ...;
IloRandom random = ...;
IloSelector<IlcIntVar,IlcIntVarArray> sel1 = RandomVariableSelector(env, random);
IloSelector<IlcIntVar,IlcIntVarArray> sel2 = RandomVariableSelector(solver, random);

An instance of a variable can then be selected as follows:

IlcIntVarArray array = ...;
IlcIntVar selected;
IlcBool isSelected = sel2.select(selected, array);

The second way to define a selector is to use the predefined class IloBestSelector, which makes the best selection based on criteria that are supplied by parameters. There are many cases where, in order to select an object from a container, all the objects in the container need to be visited, filtered by a predicate, and then compared so that the best object from this comparison will be the selected one.

Such selectors are created using instances of IloBestSelector<Object,Container> which is a subclass of IloSelector<Object,Container>. This is a flexible and modular way to build a selector and IBM recommends that, when possible, you use this approach.

The IloBestSelector constructor can take up to three parameters:

In addition, evaluators that evaluate an object and return an IloNum value can be used to build comparators. Translators, which translate objects of one type to another type, can be used to create both evaluators and predicates.

The concepts of visitor, predicate, comparator, evaluator, and translator are described in the following sections.

Visitors

A visitor IloVisitor<class Object,class Container> is a class that allows you to traverse each of the elements of type Object of a container class Container. For instance, a visitor can be used to specify how to traverse the set of variables IloIntVar of an array of variables IloIntVarArray.

Visitors can be created in three ways:

For a pair of classes <Object,Container>, you can define at most one visitor that will be considered as the default visitor for objects of type Object in container Container. By default, if no visitor is given at the construction of an instance of IloBestSelector<Object,Container>, the default visitor for the pair <Object,Container> will be used.

The following default visitors are predefined in Solver:

These visitors traverse the elements of the arrays by increasing index from 0 until array.getSize()-1.

The macro ILODEFAULTVISITOR allows you to define a new default visitor for your own classes of objects and containers. Within the code of this macro, the function visit(Object) allows you to specify the visited objects.

For example, here is how the default visitor for integer variables in an integer variable array is defined in Solver:

ILODEFAULTVISITOR(IloIntVar,IloIntVarArray,array) {
   const IloInt size = array.getSize();
   for (IloInt i=0; i<size; ++i)
      visit(array[i]);
}

The ILOVISITORi macros allow you to define a new visitor with i data members.

For instance, the visitor defined in the following sample only visits the n first variables in an array of variables, n being a data field of the visitor:

ILOVISITOR1(VisitNFirst,
	    IlcIntVar,
            IlcIntVarArray, array,
            IlcInt, n) {
   const IloInt imax = IlcMin(n, array.getSize());
   for (IloInt i=0; i<imax; ++i)
      visit(array[i]);
}

An instance of such a visitor to visit the 10 first variables of an array can then be constructed as follows:

IloVisitor<IlcIntVar,IlcIntVarArray> visitor = VisitNFirst(solver, 10);

Predicates

A predicate on objects of type Object is a class that implements a test on an object and returns an IloBool value. Predicates on objects of type Object are instances of the template class IloPredicate<Object>.

Predicates are used in selectors to filter the set of candidate objects for selection.

The test function of a predicate is the member function IloBool operator()(Object obj, IloAny nu =0). This function returns IloTrue if and only if the object satisfies the predicate. An optional context nu can be passed to the test function in cases where the result of the test depends on some contextual information. When predicates are used in an instance of IloBestSelector<Object,Container>, the selector tests each visited object passing the instance of Container as context to the test function of the predicate.

Predicates can either be created on a Concert environment (IloEnv) or on the reversible heap of a solver (IloSolver) depending on the signature that is used to construct the predicate.

There are two ways to define a predicate:

The first way to create a predicate is to use the ILOPREDICATEi macros to define new predicates with i data members. The ILOCTXPREDICATEi macros define new predicates in the same way as the ILOPREDICATEi macros, except that you can add a context.

For example, the following code instantiates a predicate that tests whether or not an integer variable is bound:

ILOPREDICATE0(IsBound, 
              IlcIntVar, v) {
   return v.isBound(); 
}

The slightly more complex predicate that follows instantiates a predicate that tests whether an instance of IloIntVar is bound or not in a solver. The solver is stored as a data field of the predicate.

ILOPREDICATE1(IsBoundInSolver,
	      IloIntVar, v,
              IloSolver, solver) {
   return solver.isBound(v);
}

An instance of such a predicate can be allocated on a Concert environment as follows:

IloEnv env;
IloSolver solver1 = ...;
IloPredicate<IloIntVar> p1 = IsBoundInSolver(env, solver1);

Testing whether a given variable v is bound in solver solver1 can then be performed as follows:

IloIntVar v = ...;
IloBool   vIsBoundInSolver1 = p1(v);

The second way to create a predicate is to compose subordinate predicates using the logical operators !, &&, and || and the function IloIfThenElse.

For instance, if pi are predicates on objects of type <Object>, a composite predicate on objects of type <Object> can be defined as follows:

IloPredicate<Object> cp = IloIfThenElse(p0, p1&&!p2, p3) || p4;

Predicate cp will be true on an instance of object Object if and only if for that instance, p4 is true or, in case p0 is true, then p1 is true and p2 is false, otherwise (if p0 is false), p3 is true.

Predicates on objects of different types cannot be composed together. This will raise an error at compilation time.

Composite predicates, when invoked with a context, will pass this context to their subordinate predicates.

Comparators

A comparator is a class that implements a comparison between two objects. Comparators of objects of type Object are instances of the template class IloComparator<Object>.

Comparators are used in selectors to compare the candidate objects that have not been filtered and select the best among them.

There are two main comparison functions of a comparator: operator() and isBetterThan.

The function operator() takes two objects, o1 and o2, and by default returns -1, 0, or 1 depending on whether o1 is respectively better, equal or worse than o2. The optional argument nu is a context that can be used for the comparison:

IloInt operator()(IloObject o1, IloObject o2, IloAny nu = 0) const;

The function isBetterThan takes two objects, o1 and o2, and returns IloTrue if and only if o1 is preferred to o2. The optional argument nu is a context that can be used for the comparison:

IloBool isBetterThan(IloObject o1, IloObject o2, IloAny nu = 0) const;

Comparators can either be created on a Concert environment (IloEnv) or on the reversible heap of a solver (IloSolver) depending on the signature that is used to construct the comparator.

There are three ways to create comparators:

The first way to define a new comparator is to use macros to define the comparator. Comparators defined with ILOCOMPARATORi define a comparator with i data fields that must return a Boolean value equal to IloTrue if and only if the comparator's left-hand side is better than its right-hand side. The ILOCTXCOMPARATORi macros define new comparators in the same way, except that you can add a context for comparison.

For example, the following comparator compares the values of two IloNumVar variables in a solution. The solution is a context of the comparison.

ILOCTXCOMPARATOR0(HasValueSmallerThan,
                  IloIntVar, v1, v2,
                  IloSolution, sol) {
   return sol.getValue(v1) < sol.getValue(v2);
}

An instance of this comparator can be created as follows:

IloEnv env;
IloComparator<IloIntVar> comp = HasValueSmallerThan(env);

And it can be invoked as follows:

IloIntVar v1 = ...;
IloIntVar v2 = ...;
IloSolution sol = ...;
IloBool v1SmallerThanv2InSol = comp(v1,v2,&sol);

Comparators defined with ILOCLIKECOMPARATORi and ILOCTXCLIKECOMPARATORi are very similar to the ones defined by ILOCOMPARATORi and ILOCTXCOMPARATORi except for the return type of the comparison. The comparators defined by the ILOCLIKECOMPARATORi and ILOCTXCLIKECOMPARATORi macros return an integer equal to -1 if the comparator's left-hand side is better than its right-hand side, an integer equal to +1 if the comparator's right-hand side is better than its left-hand side, and an integer equal to 0 in cases where they are not comparable.

The second way to define a comparator is to compose subordinate comparators. The class IloCompositeComparator allows you to define a subclass of comparator that is composed of a list of subordinate comparators. New comparators can be added to this list by using the member function add.

Two of the most useful types of comparator composition are lexical composition and Pareto composition.

The following code defines a new comparator of objects of type Object as a lexical composition of n comparators.

IloLexicalComparator<Object> lc = IloComposeLexical(c1,c2,...,cn);

The composite comparator lc will prefer an object x to an object y if and only if there exists a comparator ci in the list of subordinate comparators such that:

The following code defines a new comparator of objects of type Object as a Pareto composition of n comparators.

IloComparator<Object> pc = IloComposePareto(c1,c2,...,cn);

The composite comparator pc will prefer an object x to an object y if and only if

The third way to define a comparator is very common. The first step is to define an evaluator that evaluates the objects and returns a numeric evaluation and then compare the objects depending on this evaluation by preferring the one with the lowest or greatest evaluation.

Evaluators and evaluator-based comparators are described in the next section.

Evaluators

An evaluator of objects of type Object is a class that implements an evaluation of an object and returns an IloNum value. Evaluators of objects of type Object are instances of the template class IloEvaluator<Object>.

The evaluation function of an evaluator is the member function IloNum operator()(Object obj, IloAny nu =0). This function returns an evaluation of the instance of the object given as an argument. An optional context nu can be passed to the evaluation function in cases where the result of the evaluation depends on some contextual information.

The member functions makeLessThanComparator and makeGreaterThanComparator are available on the evaluator base class IloEvaluator to build and return a comparator based on the invoking evaluator.

Evaluators can either be created on a Concert environment (IloEnv) or on the reversible heap of a solver (IloSolver) depending on the signature that is used to construct the evaluator.

There are two ways to define an evaluator:

The first way to create an evaluator is to use the ILOEVALUATORi macros to define new evaluators with i data members. The ILOCTXEVALUATORi macros define new evaluators in the same way as the ILOEVALUATORi macros, except that you can add a context.

For example, the following code instantiates an evaluator that returns the size of a variable's domain:

ILOEVALUATOR0(DomainSize, 
              IlcIntVar, v) {
   return v.getSize(); 
}

The slightly more complex evaluator in the following sample instantiates an evaluator that returns the value of an IloIntVar in a solution. The instance of the solution is passed as a context to the evaluation function.

ILOCTXEVALUATOR0(ValueInSolution,
	         IloIntVar, v,
                 IloSolution, sol) {
   return sol.getValue(v);
}

An instance of such an evaluator can be allocated on a Concert environment as follows:

IloEnv env;
IloEvaluator<IloIntVar> eval = ValueInSolution(env);

Evaluating a given variable v in the context of a given solution sol can then be performed as follows:

IloIntVar v = ...;
IloSolution sol = ...;
IloNum value = eval(v,&sol);

The second way to define an evaluator is to compose subordinate evaluators using algebraic operators such as +, -, *, /, IloMin, and IloMax and miscellaneous functions such as IloCeil or IloFloor.

For instance, if ei are evaluators on objects of type <Object>, a composite evaluator on objects of type <Object> can be defined as follows:

IloEvaluator<Object> ce = e1 * IloMin(e2 + IloFloor(3*e3), -e4)

Evaluators on objects of different types cannot be composed together. This will raise an error at compilation time.

Composite evaluators, when invoked with a context, will pass this context to their subordinate evaluators.

Composing evaluators and predicates

Evaluators can also be composed with predicates to build new evaluators. Likewise, predicates can be built as a composition of evaluators.

For example, the function IloIfThenElse allows you to instantiate a conditional evaluator that, depending on the test performed by a predicate p, will return the value of different evaluators (e1 if p is true, e2 otherwise):

IloEvaluator<Object> ce = IloIfThenElse(p, e1, e2);

Composite predicates can also be built as a composition of evaluators with the operators ==, !=, <, <=, >= and >.

For example, the following predicate will return IloTrue on an instance of Object, if and only if, for this instance, the evaluation of e1 is lower than the evaluation of e2:

IloPredicate<Object> p = (e1<e2);

Composite evaluators and predicates, when invoked with a context, will pass this context to their subordinate evaluators or predicates.

Translators

The template class IloTranslator<ObjectOut,ObjectIn> allows you to translate any object of type ObjectIn into a unique object of type ObjectOut.

Translators can be used to create both evaluators and predicates.

A translator IloTranslator<ObjectOut,ObjectIn> defines a translation member function that, given an input object o and an optional context, returns an output object that is the translation of the object passed as argument:

ObjectOut operator()(ObjectIn o, IloAny nu=0) 

Translators can be used to define new predicates and evaluators by using the composition operator <<.

Translators can either be created on a Concert environment (IloEnv) or on the reversible heap of a solver (IloSolver) depending on the signature that is used to construct the translator.

A new translator can be defined using the macro ILOTRANSLATOR.

For example, suppose an evaluator is available that returns the size of an instance of the Solver class IlcIntSet as defined in the following sample:

ILOEVALUATOR0(SetSize,
	      IlcIntSet, set) {
   return set.getSize();
}

Now suppose that you want to define an evaluator of an IlcIntSetVar that returns the size of its required set. You could write it explicitly as follows:

ILOEVALUATOR0(RequiredSetSize,
	      IlcIntSetVar, v) {
   return v.getRequiredSet().getSize();
}

An alternative is to define an object that automatically translates an instance of IlcIntSetVar into its unique required set and then compose this translator with the evaluator of IlcIntSet to build a new evaluator of IlcIntSetVar.

For instance in the above example, a translator that returns the required set of an IlcIntSetVar can be defined as follows:

ILOTRANSLATOR(RequiredSetTranslator,
              IlcIntSet,
              IlcIntSetVar, v) {
  return v.getRequiredSet();
}

The above translator that returns the required set of an IlcIntSetVar can then be composed with the evaluator of IlcIntSet to build a new evaluator of IlcIntSetVar:

IloEvaluator<IlcIntSet> sizeOfSet = SetSize(solver);
IloTranslator<IlcIntSet,IlcIntSetVar> requiredSet = RequiredSetTranslator(solver);
IloEvaluator<IlcIntSetVar> requiredSetSize = sizeOfSet << requiredSet;

Note that such composed evaluators and predicates, when invoked with a context, will pass this context to their subordinate evaluators/predicates and translators.

Example of a Selector

To demonstrate the concepts explained in this section, the following code sample gives a full example of a complex selector of IlcIntSetVar in an IlcIntSetVarArray that:

ILOVISITOR0(VarWithEvenIndex, 
	    IlcIntSetVar,
            IlcIntSetVarArray, array) {
   const IloInt size = array.getSize();
   for (IloInt i=0; i<size; i+=2)
      visit(array[i]);
}

ILOPREDICATE0(IsBound, 
              IlcIntSetVar, v) {
   return v.isBound(); 
}

ILOEVALUATOR0(SetSize,
	      IlcIntSet, set) {
   return set.getSize();
}

ILOTRANSLATOR(RequiredSetTranslator,
              IlcIntSet,
              IlcIntSetVar, v) {
  return v.getRequiredSet();
}

ILOTRANSLATOR(PossibleSetTranslator,
              IlcIntSet,
              IlcIntSetVar, v) {
  return v.getPossibleSet();
}

IloSolver s = ...;

IloEvaluator<IlcIntSetVar> sizeReq = SetSize(s) << RequiredSetTranslator(s);
IloEvaluator<IlcIntSetVar> sizePos = SetSize(s) << PossibleSetTranslator(s);

IloSelector<IlcIntSetVar,IlcIntSetVarArray> selector = 
  IloBestSelector(VarWithEvenIndex(s),
                  !IsBound(s) && (0 < sizeReq), 
                  IloComposeLexical((sizePos-sizeReq).makeLessThanComparator(), sizePos.makeLessThanComparator()));

See Also

IloBestSelector, IloComparator, IloCompositeComparator, IloEvaluator, IloLexicographicComparator, IloParetoComparator, IloPredicate, IloSelector, IloTranslator, IloVisitor, ILOCLIKECOMPARATOR0, ILOCOMPARATOR0, ILODEFAULTVISITOR, ILOEVALUATOR0, ILOPREDICATE0, ILOSELECTOR0, ILOTRANSLATOR, ILOVISITOR0, IloCeil, IloComposeLexical, IloComposePareto, IloFloor, IloIfThenElse, IloMax, IloMin.

PREVIOUS NEXT