With the classes you have defined for this model, and with the accessors for attributes of objects that are elements in the domains of sets, you can now represent the constraints of the problem. In fact, most of the constraints mentioned in the problem description can be added to the model by the constructors of the classes you just defined. You will be using the predefined Concert Technology constraints IloEqSum
, IloEqMin
, IloEqMax
, and IloEqUnion
to define the problem constraints on set variables.
Constraints in the Tank constructor
In the assignments of orders to trucks, you must assign products to tanks while respecting the constraint that you must not mix products of different types within a tank. To make sure a tank contains only products of the same type, regardless of which order the product fulfills, you add these constraints in the constructor of Tank
:
model.add( IloEqMin(env, _prodOrders, _type, PType(env)) );
model.add( IloEqMax(env, _prodOrders, _type, PType(env)) );
|
The capacity of a tank is taken into account in the constructor of the _load
variable. The following constraint uses IloEqSum
to compute the load of a given tank:
model.add( IloEqSum(env, _prodOrders, _load, PQuantity(env)) );
|
Constraints in the Truck constructor
In the model, a truck is used if an order is assigned to it. If a truck is not used, you must not assign an order to it. As you continue to define constraints in the class constructors, you use the cardinality variable (supplied by the predefined Solver function IloCard
) of the set variable _orders
to indicate whether or not a particular truck is used, like this:
_used = IloBoolVar(env);
model.add( _used == (IloCard(_orders) > 0));
|
Likewise, in the constructor of TruckI
, you express the idea that the two products 3 and 4
are incompatible and thus cannot be loaded on the same truck by adding a constraint on the set of different product types assigned to the tanks of the trucks. You define a constrained set variable ttanks
; its domain represents the types of the possible products. Then, you use the predefined Concert Technology function IloEqUnion
to constrain ttanks
to correspond to the types of the possible products for the possible tanks of the truck at hand.
IloNumSetVar ttanks(env, IloIntArray(env, 5, 1, 2, 3, 4, 5));
model.add(IloEqUnion(env, _tankVar, ttanks, TankType(env)));
model.add( ! (IloMember(env, 3, ttanks) && IloMember(env, 4, ttanks)) );
|
You define a set variable tprodVar
to represent the products in the tanks of the truck. To do so, you use the predefined Solver constraint IloEqUnion
again with TankProducts
, the accessor you defined in "Accessing the products in a tank: TankProductI", in this way:
IloAnySetVar tprodVar(env, prodOrdersArray);
model.add( IloEqUnion(env, _tankVar, tprodVar, TankProducts(env) ));
|
Likewise, you define a constrained set variable oprodVar
to represent the products in the orders fulfilled by a truck, and you use IloEqUnion
with OrderProducts
to take the union of products in orders assigned to the truck, like this:
IloAnySetVar oprodVar(env, prodOrdersArray);
model.add( IloEqUnion(env, _orders, oprodVar, OrderProducts(env) ));
|
Then you combine those two set variables in a constraint to make sure that the products filling the tanks on a truck are exactly the products demanded in the orders fulfilled by that truck.
model.add( tprodVar == oprodVar );
|
To be sure that all the products of the orders fulfilled by the truck are assigned to one and only one tank of the truck, you define a partition constraint between the previously defined set variable oprodVar
(that represents the products assigned globally to the truck through the orders) and the set variable prodVars[i]
of each possible tank of the truck (that represents the products assigned to that particular tank). You use the predefined Solver constraint IloEqPartition
like this:
model.add( IloEqPartition(env, oprodVar, prodVars ) );
|
Also in the constructor of TruckI
, you enforce the stipulation that no more than three different types of products can be loaded on the same truck. To do so, you directly constrain the cardinality IloCard
of the variable ttanks
which represents this set of types, like this:
model.add( IloCard( ttanks ) <= 3 );
|
To insure that the 12000-unit limit on the maximum load of a truck is respected, you use the predefined constraint IloEqSum
to accumulate the quantities specified in orders assigned to a given truck. You add a constraint that the cumulative load must be less than or equal to the limit, and you also add a constraint (again using IloEqSum
) that the load on the truck is the same as the sum of the loads in its tanks. All those steps occur in the constructor of TruckI
, like this:
IloIntVar load(env, 0, 12000);
model.add( IloEqSum(env, _orders, load, OQuantity(env) ));
model.add( IloEqSum(env, _tankVar, load, TankLoad(env) ));
|