package vroom.trsp.instances;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import umontreal.iro.lecuyer.probdist.DiscreteDistributionInt;
import umontreal.iro.lecuyer.probdist.UniformIntDist;
import umontreal.iro.lecuyer.rng.MRG32k3a;
import umontreal.iro.lecuyer.rng.RandomStream;
import vroom.common.modeling.dataModel.Depot;
import vroom.common.modeling.dataModel.IVRPInstance;
import vroom.common.modeling.dataModel.IVRPRequest;
import vroom.common.modeling.dataModel.Vehicle;
import vroom.common.modeling.dataModel.attributes.ITimeWindow;
import vroom.common.modeling.dataModel.attributes.NodeAttributeKey;
import vroom.common.modeling.dataModel.attributes.PointLocation;
import vroom.common.modeling.dataModel.attributes.RequestAttributeKey;
import vroom.common.modeling.dataModel.attributes.SimpleTimeWindow;
import vroom.common.modeling.io.SolomonPersistenceHelper;
import vroom.common.utilities.Utilities;
import vroom.common.utilities.ssj.RandomGeneratorManager;
import vroom.trsp.datamodel.TRSPRequest;
import vroom.trsp.datamodel.Technician;
import vroom.trsp.datamodel.TechnicianFleet;
import vroom.trsp.io.PillacSimplePersistenceHelper;
import vroom.trsp.io.TRSPSolomonLegacyPersistenceHelper;
import vroom.trsp.legacy.TRSPLegacyInstance;
/**
* The Class <code>SolomonBasedInstanceGenerator</code> is used to generate TRSP instances based on Solomon instances.
* <p>
* Creation date: Feb 11, 2011 - 10:42:39 AM.
*
* @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a
* href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a
* href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a>
* @version 1.0
*/
public class SolomonBasedInstanceGenerator {
public enum Mode {
NORMAL("../Instances/trsp/pillac/%s.txt"), //
SIZE25("../Instances/trsp/pillac/25/%s.txt"), //
TOY("../Instances/trsp/toy/%s.txt"), //
CVRPTW("../Instances/trsp/cvrptw/%s.txt");
public final String filePattern;
/**
* Creates a new <code>Mode</code>
*
* @param file
*/
private Mode(String file) {
this.filePattern = file;
}
}
// private static final String SRC_FOLDER = "../Instances/cvrptw/solomon_25";
// private static final String SRC_FOLDER = "../Instances/cvrptw/solomon_50";
private static final String SRC_FOLDER = "../Instances/cvrptw/solomon";
private static final String SOLOMON_BKS = "../Instances/cvrptw/solomon.sol";
private static final Mode MODE = Mode.CVRPTW;
// Expected number of request per technician
public static final int sReqPerTech = 8;
// Total number of skills
public static final int sSkillCount = 5;
// Total number of tools
public static final int sToolCount = 5;
// Total number of spare parts
public static final int sSpareCount = 5;
// Number of skills for each technician
public static final DiscreteDistributionInt sTechSkillsDist = new UniformIntDist(2, sSkillCount - 1);
// Number of tools for each technician
public static final DiscreteDistributionInt sTechToolsDist = new UniformIntDist(0, sToolCount);
// Number of spare parts for each technician
public static final DiscreteDistributionInt sTechSpareCountDist = new UniformIntDist(2, sSpareCount);
// Number of spare parts of each type for each technician
public static final DiscreteDistributionInt sTechSpareDist = new UniformIntDist(2, 5);
// Number of skills for each request
public static final DiscreteDistributionInt sReqSkillsDist = new UniformIntDist(1, 1);
// Number of tools for each request
public static final DiscreteDistributionInt sReqToolsDist = new UniformIntDist(0, 2);
// Number of spare parts for each request
public static final DiscreteDistributionInt sReqSpareCountDist = new UniformIntDist(0, 2);
// Number of spare parts of each type for each request
public static final DiscreteDistributionInt sReqSpareDist = new UniformIntDist(1, 1);
private final int reqPerTech;
private final Mode mode;
private final RandomStream stream;
private final int skillCount;
private final int toolCount;
private final int spareCount;
private final DiscreteDistributionInt techSkillsDist;
private final DiscreteDistributionInt techToolsDist;
private final DiscreteDistributionInt techSpareCountDist;
private final DiscreteDistributionInt techSpareDist;
private final DiscreteDistributionInt reqSkillsDist;
private final DiscreteDistributionInt reqToolsDist;
private final DiscreteDistributionInt reqSpareCountDist;
private final DiscreteDistributionInt reqSpareDist;
private final RandomGeneratorManager rndGen;
private int[][] toolSets;
private int[][] spareSets;
/**
* Creates a new <code>SolomonBasedInstanceGenerator</code>
*
* @param stream
* @param skillCount
* @param toolCount
* @param spareCount
* @param techSkillsDist
* @param techToolsDist
* @param techSpareCountDist
* @param techSpareDist
* @param reqSkillsDist
* @param reqToolsDist
* @param reqSpareCountDist
* @param reqSpareDist
*/
public SolomonBasedInstanceGenerator(Mode mode, RandomStream stream, int reqPerTech, int skillCount, int toolCount,
int spareCount, DiscreteDistributionInt techSkillsDist, DiscreteDistributionInt techToolsDist,
DiscreteDistributionInt techSpareCountDist, DiscreteDistributionInt techSpareDist,
DiscreteDistributionInt reqSkillsDist, DiscreteDistributionInt reqToolsDist,
DiscreteDistributionInt reqSpareCountDist, DiscreteDistributionInt reqSpareDist) {
this.mode = mode;
this.stream = stream;
this.skillCount = mode == Mode.CVRPTW ? 0 : skillCount;
this.toolCount = mode == Mode.CVRPTW ? 0 : toolCount;
this.spareCount = mode == Mode.CVRPTW ? 1 : spareCount;
this.techSkillsDist = techSkillsDist;
this.techToolsDist = techToolsDist;
this.techSpareCountDist = techSpareCountDist;
this.techSpareDist = techSpareDist;
this.reqSkillsDist = reqSkillsDist;
this.reqToolsDist = reqToolsDist;
this.reqSpareCountDist = reqSpareCountDist;
this.reqSpareDist = reqSpareDist;
this.reqPerTech = reqPerTech;
rndGen = new RandomGeneratorManager(stream);
}
/**
* Generate a TRSP instance from an original VRPTW instance
*
* @param instance
* @return
*/
public TRSPLegacyInstance generateInstance(IVRPInstance instance) {
generateSubsets();
int fleetSize = (int) Math.ceil(((double) instance.getRequestCount()) / reqPerTech);
if (mode == Mode.CVRPTW) {
fleetSize = instance.getFleet().size();
// try {
// BestKnownSolutions bks = new BestKnownSolutions(SOLOMON_BKS);
// fleetSize = bks.getIntValue(instance.getName() + "." + instance.getRequestCount(), "K");
// } catch (Exception e) {
// // e.printStackTrace();
// }
}
// Generate requests
Collection<TRSPRequest> requests = generateRequests(instance, fleetSize);
// Generate crew
List<Technician> technicians = generateCrew(instance, requests, fleetSize);
// Depots
ArrayList<Depot> depots = new ArrayList<Depot>(technicians.size() + 1);
if (mode == Mode.TOY)
instance.getDepot(0).setAttribute(NodeAttributeKey.TIME_WINDOW, new SimpleTimeWindow(0, 9999));
depots.add(instance.getDepot(0));
for (Technician t : technicians) {
depots.add(t.getHome());
}
// Define the new instance
String name;
switch (mode) {
case CVRPTW:
name = String.format("%s.%s", instance.getName(), instance.getRequestCount());
break;
default:
name = String.format("%s.%s_%s-%s-%s-%s", instance.getName(), instance.getRequestCount(), reqPerTech,
skillCount, toolCount, spareCount);
break;
}
TRSPLegacyInstance trspInstance = new TRSPLegacyInstance(
//
name, instance.getID(), TechnicianFleet.newTechnicianFleet(technicians), depots, skillCount, toolCount,
spareCount, instance.getCostDelegate());
trspInstance.addRequests(Utilities.<TRSPRequest, IVRPRequest> convertToList(requests));
return trspInstance;
}
private List<Technician> generateCrew(IVRPInstance instance, Collection<TRSPRequest> requests, int fleetSize) {
ArrayList<Technician> technicians = new ArrayList<Technician>(fleetSize);
TechnicianComparator comp = new TechnicianComparator(instance);
// Generate technician skills and tools
// for (int id = 0; id < instance.getFleet().size(); id++) {
for (int id = 0; id < fleetSize; id++) {
Technician tec = generateTechnician(instance.getFleet().getVehicle(), id, instance);
technicians.add(tec);
}
if (mode == Mode.CVRPTW)
return technicians;
comp.update(technicians, requests);
int attempts = 0;
if (!comp.mUnfeasibleRequests.isEmpty()) {
System.out.printf(" ... fixing %s infeasible requests ... ", comp.mUnfeasibleRequests.size());
}
while (!comp.mUnfeasibleRequests.isEmpty()) {
Collections.sort(technicians, comp);
if (attempts > 100) {
Set<TRSPRequest> old = new HashSet<TRSPRequest>(comp.mUnfeasibleRequests);
int i = technicians.size() - 1;
for (TRSPRequest r : comp.mUnfeasibleRequests) {
Technician t = technicians.get(i);
t = generateTechnician(t, t.getID(), instance, r);
technicians.set(i, t);
i--;
}
comp.update(technicians, requests);
for (TRSPRequest r : old)
if (comp.mUnfeasibleRequests.contains(r))
throw new IllegalStateException("Cannot generate a technician able to serve request " + r);
attempts = 0;
} else {
Technician t = technicians.remove(technicians.size() - 1);
technicians.add(generateTechnician(t, t.getID(), instance));
comp.update(technicians, requests);
}
attempts++;
}
if (!comp.mUnfeasibleRequests.isEmpty()) {
System.out.print(" ... ok ... ");
}
return technicians;
}
/**
* Generate a technician from the given original vehicle.
*
* @param original
* the original
* @param id
* the id
* @param instance
* the instance
* @return the technician
*/
private Technician generateTechnician(Vehicle original, int id, IVRPInstance instance) {
int[] tecSkills;
int[] tecTools;
int[] tecSpareId;
int[] tecSpare;
if (mode == Mode.CVRPTW) {
tecSkills = new int[skillCount];
for (int i = 0; i < tecSkills.length; i++)
tecSkills[i] = i;
tecTools = new int[toolCount];
for (int i = 0; i < tecTools.length; i++)
tecTools[i] = i;
tecSpare = new int[] { (int) original.getCapacity() };
} else {
if (mode == Mode.TOY) {
tecSkills = new int[skillCount];
for (int i = 0; i < tecSkills.length; i++)
tecSkills[i] = i;
}
tecSkills = generateSubset(rndGen.nextInt(techSkillsDist), skillCount, stream);
tecTools = generateSubset(toolSets, tecSkills, rndGen.nextInt(techToolsDist), stream);
tecSpareId = generateSubset(spareSets, tecSkills, rndGen.nextInt(techSpareCountDist), stream);
tecSpare = new int[spareCount];
for (int s = 0; s < tecSpareId.length; s++) {
tecSpare[tecSpareId[s]] = rndGen.nextInt(techSpareDist);
}
}
// Generate the technician home
int homeX = mode == Mode.CVRPTW ? (int) instance.getDepot(0).getLocation().getX() : stream.nextInt(0, 100);
int homeY = mode == Mode.CVRPTW ? (int) instance.getDepot(0).getLocation().getY() : stream.nextInt(0, 100);
Depot home = new Depot(id + 1, new PointLocation(homeX, homeY));
if (mode == Mode.TOY)
home.setAttribute(NodeAttributeKey.TIME_WINDOW, new SimpleTimeWindow(0, 9999));
else
home.setAttribute(NodeAttributeKey.TIME_WINDOW, instance.getDepot(0).getTimeWindow());
// Create a new technician
Technician tec = new Technician(id, "" + id, original.getFixedCost(), original.getVariableCost(), 1, tecSkills,
tecTools, tecSpare, home);
return tec;
}
/**
* Generate a technician for a specific request.
*
* @param original
* the original
* @param id
* the id
* @param instance
* the instance
* @param request
* the request
* @return the technician
*/
private Technician generateTechnician(Vehicle original, int id, IVRPInstance instance, TRSPRequest request) {
// The skill set is initialized from the request set
int[] tecSkills = generateSubset(rndGen.nextInt(techSkillsDist), skillCount, stream);
TreeSet<Integer> skills = new TreeSet<Integer>(request.getSkillSet().toList());
int s = 0;
// Add more skills
while (s < tecSkills.length && skills.size() < tecSkills.length) {
if (!skills.contains(tecSkills[s]))
skills.add(tecSkills[s]);
s++;
}
tecSkills = new int[skills.size()];
s = 0;
for (int i : skills) {
tecSkills[s++] = i;
}
Arrays.sort(tecSkills);
int[] tecTools = generateSubset(toolSets, tecSkills, rndGen.nextInt(techToolsDist), stream);
TreeSet<Integer> tools = new TreeSet<Integer>(request.getToolSet().toList());
s = 0;
// Add more skills
while (s < tecTools.length && tools.size() < tecTools.length) {
if (!tools.contains(tecTools[s]))
tools.add(tecTools[s]);
s++;
}
tecTools = new int[tools.size()];
s = 0;
for (int i : tools) {
tecTools[s++] = i;
}
Arrays.sort(tecTools);
int[] tecSpareId = generateSubset(spareSets, tecSkills, rndGen.nextInt(techSpareCountDist), stream);
int[] tecSpare = new int[spareCount];
for (s = 0; s < tecSpareId.length; s++) {
tecSpare[tecSpareId[s]] = rndGen.nextInt(techSpareDist);
}
// Ensure that the technician has the required spare parts
for (int i = 0; i < tecSpare.length; i++) {
tecSpare[i] = Math.max(tecSpare[i], request.getSparePartRequirement(i));
}
// Generate the technician home
int homeX = (int) request.getNode().getLocation().getX();
int homeY = (int) request.getNode().getLocation().getY();
Depot home = new Depot(id + 1, new PointLocation(homeX, homeY));
if (mode == Mode.TOY)
home.setAttribute(NodeAttributeKey.TIME_WINDOW, new SimpleTimeWindow(0, 9999));
else
home.setAttribute(NodeAttributeKey.TIME_WINDOW, instance.getDepot(0).getTimeWindow());
// Create a new technician
Technician tec = new Technician(id, "" + id, original.getFixedCost(), original.getVariableCost(), 1, tecSkills,
tecTools, tecSpare, home);
return tec;
}
/**
* Generate the tools and spare parts subsets that will be used to generate technicians and requests
*/
private void generateSubsets() {
// Generate the sets of tools associated with each skill
toolSets = new int[skillCount][];
int maxToolsPerSkill = (int) ((3f * toolCount) / skillCount);
int minToolsPerSkill = (int) ((2f * toolCount) / skillCount);
// int numTools = (int) Math.ceil(1f * toolCount / skillCount);
for (int s = 0; s < skillCount; s++) {
// Number of tools
int numTools = stream.nextInt(minToolsPerSkill, maxToolsPerSkill);
toolSets[s] = generateSubset(numTools, toolCount, stream);
// toolSets[s] = new int[numTools];
// for (int i = 0; i < numTools; i++) {
// toolSets[s][i] = s * numTools + i;
// }
}
// Generate the sets of spare parts associated with each skill
spareSets = new int[skillCount][];
int maxSparePerSkill = (int) ((2f * toolCount) / skillCount);
int minSparePerSkill = (int) ((1f * toolCount) / skillCount);
// int numSpare = (int) Math.ceil(1f * spareCount / skillCount);
for (int s = 0; s < skillCount; s++) {
// Number of spare parts
int numSpare = stream.nextInt(minSparePerSkill, maxSparePerSkill);
spareSets[s] = generateSubset(numSpare, spareCount, stream);
// spareSets[s] = new int[numSpare];
// for (int i = 0; i < numSpare; i++) {
// spareSets[s][i] = s * numSpare + i;
// }
}
}
/**
* Generate a list of TRSPRequest from a given instance
*
* @param instance
* the base instance
* @return
*/
private Collection<TRSPRequest> generateRequests(IVRPInstance instance, int fleetSize) {
Collection<TRSPRequest> requests = new LinkedList<TRSPRequest>();
for (IVRPRequest request : instance.getRequests()) {
int[] reqSkills = generateSubset(rndGen.nextInt(reqSkillsDist), skillCount, stream);
int[] reqTools = generateSubset(toolSets, reqSkills, rndGen.nextInt(reqToolsDist), stream);
int[] reqSpareId = generateSubset(spareSets, reqSkills, rndGen.nextInt(reqSpareCountDist), stream);
int[] reqSpare;
if (mode == Mode.CVRPTW) {
reqSpare = new int[] { (int) request.getDemand() };
} else {
reqSpare = new int[spareCount];
for (int s = 0; s < reqSpareId.length; s++) {
reqSpare[reqSpareId[s]] = rndGen.nextInt(reqSpareDist);
}
}
ITimeWindow tw = mode == Mode.TOY ? new SimpleTimeWindow(0, 9999) : request
.getAttribute(RequestAttributeKey.TIME_WINDOW);
TRSPRequest r = new TRSPRequest(request.getID() + fleetSize, request.getNode(), reqSkills, reqTools,
reqSpare, tw, (int) request.getAttribute(RequestAttributeKey.SERVICE_TIME).getDuration());
requests.add(r);
}
return requests;
}
/**
* @param numElements
* the number of elements for the subset
* @param parentSetSize
* the size of the parent set
* @param stream
* @return <code>numElements</code> taken from the set <code>{0,..,parentSetSize-1}</code>
*/
public static int[] generateSubset(int numElements, int parentSetSize, RandomStream stream) {
if (parentSetSize == 0)
return new int[0];
if (numElements > parentSetSize) {
int[] set = new int[parentSetSize];
for (int i = 0; i < set.length; i++) {
set[i] = i;
}
}
LinkedList<Integer> pool = new LinkedList<Integer>();
for (int i = 0; i < parentSetSize; i++) {
pool.add(i);
}
int count = 0;
int[] set = new int[numElements];
while (count < numElements && !pool.isEmpty()) {
int id = stream.nextInt(0, pool.size() - 1);
set[count++] = pool.remove(id);
}
Arrays.sort(set);
return set;
}
/**
* @param sets
* an array containing the different available sets
* @param indexSubset
* an array containing the indexes of the availables sets in <code>set</code>
* @param numElements
* the number of elements to generate
* @param stream
* @return a subset of size <code>numElements</code> of the union of subsets defined by <code>sets</code> and
* <code>indexSubset</code>
*/
public static int[] generateSubset(int[][] sets, int[] indexSubset, int numElements, RandomStream stream) {
if (numElements == 0 || sets.length == 0)
return new int[0];
Set<Integer> union = new HashSet<Integer>();
for (int i : indexSubset) {
for (int e : sets[i]) {
union.add(e);
}
}
int[] set;
if (union.size() < numElements) {
set = new int[union.size()];
int i = 0;
for (int e : union) {
set[i++] = e;
}
} else {
LinkedList<Integer> pool = new LinkedList<Integer>(union);
int count = 0;
set = new int[numElements];
while (count < numElements && !pool.isEmpty()) {
int id = stream.nextInt(0, pool.size() - 1);
set[count++] = pool.remove(id);
}
}
Arrays.sort(set);
return set;
}
public static void main(String[] args) {
RandomStream stream = new MRG32k3a("rnd");
File instanceDir = new File(SRC_FOLDER);
String[] children = instanceDir.list();
LinkedList<File> instances = new LinkedList<File>();
for (String file : children) {
if (!file.startsWith(".") && !file.startsWith("1-")) {
try {
instances.add(new File(instanceDir.getAbsolutePath() + File.separator + file));
} catch (Exception e) {
System.err.printf("Error when reading %s : %s", file, e.getMessage());
}
}
}
// instances.add(new File(SRC_FOLDER + "/C106.txt"));
System.out.println("Mode : " + MODE);
generateInstances(MODE, instances, sReqPerTech, sSkillCount, sToolCount, sSpareCount, sTechSkillsDist,
sTechToolsDist, sTechSpareCountDist, sTechSpareDist, sReqSkillsDist, sReqToolsDist, sReqSpareCountDist,
sReqSpareDist, stream);
}
public static void generateInstances(Mode mode, List<File> instanceFiles, int reqPerTech, int skillCount,
int toolCount, int spareCount, DiscreteDistributionInt techSkillsDist,
DiscreteDistributionInt techToolsDist, DiscreteDistributionInt techSpareCountDist,
DiscreteDistributionInt techSpareDist, DiscreteDistributionInt reqSkillsDist,
DiscreteDistributionInt reqToolsDist, DiscreteDistributionInt reqSpareCountDist,
DiscreteDistributionInt reqSpareDist, RandomStream stream) {
if (mode == Mode.CVRPTW) {
spareCount = 1;
}
SolomonPersistenceHelper reader = new SolomonPersistenceHelper();
TRSPSolomonLegacyPersistenceHelper writer = new TRSPSolomonLegacyPersistenceHelper();
PillacSimplePersistenceHelper tester = new PillacSimplePersistenceHelper();
SolomonBasedInstanceGenerator gen = new SolomonBasedInstanceGenerator(mode, stream, reqPerTech, skillCount,
toolCount, spareCount, techSkillsDist, techToolsDist, techSpareCountDist, techSpareDist, reqSkillsDist,
reqToolsDist, reqSpareCountDist, reqSpareDist);
for (File i : instanceFiles) {
System.out.printf("Converting instance %s ... ", i.getPath());
IVRPInstance vrptwInstance;
try {
vrptwInstance = reader.readInstance(i);
TRSPLegacyInstance trspInstance = gen.generateInstance(vrptwInstance);
File dest = new File(String.format(mode.filePattern, trspInstance.getName()));
writer.writeInstance(trspInstance, dest, null);
System.out.printf("writen to file %s\n", dest.getName());
tester.readInstance(dest, false);
} catch (IOException e) {
e.printStackTrace();
break;
} catch (IllegalStateException e) {
e.printStackTrace();
continue;
}
}
System.out.println("DONE");
}
private static class TechnicianComparator implements Comparator<Technician> {
private final Map<Technician, Integer> mEvaluations;
private final IVRPInstance mInstance;
private final Set<TRSPRequest> mUnfeasibleRequests;
private TechnicianComparator(IVRPInstance instance) {
mEvaluations = new HashMap<Technician, Integer>();
mUnfeasibleRequests = new HashSet<TRSPRequest>();
mInstance = instance;
}
private void update(Collection<Technician> technicians, Collection<TRSPRequest> requests) {
mEvaluations.clear();
mUnfeasibleRequests.clear();
mUnfeasibleRequests.addAll(requests);
for (TRSPRequest r : requests) {
for (Technician t : technicians) {
int eval = mEvaluations.containsKey(t) ? mEvaluations.get(t) : 0;
if (r.getSkillSet().isCompatibleWith(t.getSkillSet())) {
double arrivalTime = t.getHome().getTimeWindow().startAsDouble();
double returnTime;
// The technician can serve the request
if (r.getToolSet().isCompatibleWith(t.getToolSet())
&& Utilities.compare(r.getSparePartRequirements(), t.getSpareParts()) <= 0) {
// The technician already has the tools to serve the request directly
// Check if time windows are compatible
arrivalTime += mInstance.getCostDelegate().getCost(t.getHome(), r.getNode(), t);
returnTime = r.getTimeWindow().getEarliestStartOfService(arrivalTime) + r.getServiceTime()
+ mInstance.getCostDelegate().getCost(r.getNode(), t.getHome(), t);
} else {
// The technician does not have the tools to serve the request directly, must go to central
// depot first
// Check if time windows are compatible
arrivalTime += mInstance.getCostDelegate().getCost(t.getHome(), mInstance.getDepot(0), t)
+ mInstance.getCostDelegate().getCost(mInstance.getDepot(0), r.getNode(), t);
returnTime = r.getTimeWindow().getEarliestStartOfService(arrivalTime) + r.getServiceTime()
+ mInstance.getCostDelegate().getCost(r.getNode(), t.getHome(), t);
}
if (r.getTimeWindow().isFeasible(arrivalTime)
&& t.getHome().getTimeWindow().isFeasible(returnTime)) {
// The request can be served
eval++;
mUnfeasibleRequests.remove(r);
}
}
mEvaluations.put(t, eval);
}
}
}
@Override
public int compare(Technician o1, Technician o2) {
return mEvaluations.get(o2) - mEvaluations.get(o1);
}
}
}