Formatting output

After completing this module the student should:


Video

Transcript

Recall the following code from the module teaching for loops:

// Sets of vehicles and resources
{string} vehicles = {"Trucks","Taxis","Busses"};
{string} toBeTransported = {"Cargo","Passenger","Wheelchairs"};

// Problem data
int capacities[toBeTransported][vehicles] = [[100,1,10],[1,4,50],[0,2,1]];
int neededCapacity[toBeTransported] = [1000,800,20];

// A list of the costs of the vehicles. One entry per "vehicle"
int cost[vehicles] = [10000, 1100, 10000];

// How many vehicles to use of each type?
dvar int+ nUsed[vehicles];

minimize
sum(v in vehicles) cost[v]*nUsed[v];
subject to {
  forall(t in toBeTransported)
  sum(v in vehicles) nUsed[v]*capacities[t][v] >= neededCapacity[t];
};

Formatting output

Solving this model will give the solution:

nUsed = [9 11 15]

This is easy enough to interpret if you recall the order the vehicles were defined in. Sometimes the output is not so easy to interpret, and we need a small section of code to print out more details.

To make OPL Studio execute code, you need an execute environment:

execute NameYouChoose {
  // Code goes here
}

If you are familiar with Java, there are some similarities. For now you may use examples here and work from that:

execute WriteSolution {
  writeln("These are the vehicles used");
  for(var v in vehicles){
    if(nUsed[v]>0){
      writeln("Using "+nUsed[v]+" "+v);
      for(var t in toBeTransported){
        writeln("To transport up to "+nUsed[v]*capacities[t][v]+" of "+neededCapacity[t]+" "+t);
      }
    }
  }
}

Lets go through it line by line:

writeln("These are the vehicles used");

Will write These are the vehicles used to the scripting log tab.

for(var v in vehicles){
  // Code
}

Is a variant of the for loop, that you must use in an execute environment. The code inside will be repeated for v="Truck", v="Taxis" and v="Busses".

if(nUsed[v]>0){
    // Code
}

Will make sure that the code will only be printed if we actually use the vehicle v.

writeln("Using "+nUsed[v]+" "+v);

Will print out Using 9 Trucks for v="Truck" etc. Note that the code relies on decision variables. This requires that the execute environment is after the objective function and the constraints, as the answer otherwise would not be computed yet.

  writeln("To transport up to "+nUsed[v]*capacities[t][v]+" of "+neededCapacity[t]+" "+t);

Will (for t="Cargo", and vehicle v="Trucks") print To transport up to 900 of 1000 Cargo etc.

Precomputing data

Execute environments can also be placed before the model to be solved. In these cases, it is usually done to preprocess data.

Take for example a list of times in the format hhmm (1701 is one minute past 5 PM):

int readyTime[VISITS] = ...;            // The ready time for each visit

That is convenient to read by humans, but for the model we would rather have minutes from midnight. This can be implemented thusly:

float adjustedReadyTimes[VISITS];
execute calcTimeInMin{
  for(var v in VISITS)
  {
    adjustedReadyTimes[v] = (Opl.floor(readyTime[v]/100))*60 + (readyTime[v]%100);
  }
}

The line

float adjustedReadyTimes[VISITS];

Defines the variables we are going to use for the adjusted times.

adjustedReadyTimes[v] = (Opl.floor(readyTime[v]/100))*60 + (readyTime[v]%100);

Does the conversion. Opl.floor( ... ) is a build in method to round down values in an execute environment. (Opl.floor(readyTime[v]/100))*60 will thus compute the number of minutes hidden in the number of hours, and readyTime[v]%100 is the number of minutes with the hours filtered out.

For readyTime[v]=1701 the computation would look like this:

(Opl.floor(readyTime[v]/100))*60 + (readyTime[v]%100)
(Opl.floor(1701/100))*60 + (1701%100)
(Opl.floor(1701/100))*60 + 1 // % is called modulo, look it up if needed.
(Opl.floor(17.01))*60 + 1
(Opl.floor(17.01))*60 + 1
17*60 + 1
1020 + 1
1021

We now know that 17:01 is 1021 minutes from midnight, making durations much easier to compute in the model.