/**
*
*/
package vroom.trsp.optimization.alns;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import vroom.common.heuristics.alns.IDestroy;
import vroom.common.utilities.IntegerSet;
import vroom.common.utilities.math.QuickSelect;
import vroom.common.utilities.optimization.IParameters;
import vroom.trsp.datamodel.TRSPSolution;
import vroom.trsp.datamodel.TRSPTour;
/**
* <code>DestroyRelated</code> is a generic implementation of {@link IDestroy} for relatedness based destroy procedures
* <p>
* Creation date: May 26, 2011 - 11:25:03 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 abstract class DestroyRelated extends DestroyTRSP {
/** the randomization parameter *. */
private final double mRandomization;
/**
* Getter for the randomization parameter <em>p</em>.
* <p>
* p ≥ 1 is a parameter that controls the level of randomness (the lower p, the more randomness is introduced)
* </p>
*
* @return the parameter <em>p</em> that control the level of randomization
*/
public double getRandomization() {
return this.mRandomization;
}
/**
* Creates a new <code>DestroyRelated</code>
*
* @param randomization
* the parameter <em>p</em> that control the level of randomization (p≥1, p=1 for no randomization)
*/
public DestroyRelated(double randomization) {
super();
mRandomization = randomization;
}
/*
* (non-Javadoc)
* @see vroom.common.heuristics.alns.IDestroy#destroy(vroom.common.utilities.optimization.ISolution,
* vroom.common.utilities.optimization.IParameters, double)
*/
@Override
public Set<Integer> doDestroy(TRSPSolution solution, IParameters parameters, List<Integer> removableReq, int numReq) {
if (solution.getUnservedCount() == solution.getInstance().getRequestCount())
return Collections.emptySet();
TRSPTour lastModifiedTour = null;
// Set of candidate requests to remove
IntegerSet candidates = new IntegerSet(removableReq);
// Set of removed requests
ArrayList<Integer> remRequests = new ArrayList<Integer>(numReq);
// Initialize data structures (delegated to subclasses)
initialize(solution, parameters, numReq, candidates);
// Select the initial seed request
int seedIdx = parameters.getRandomStream().nextInt(0, candidates.size() - 1);
int seed = removableReq.get(seedIdx);
// Remove the seed
lastModifiedTour = removeRequest(solution, seed);
if (lastModifiedTour != null) {
remRequests.add(seed);
candidates.remove(seed);
requestRemoved(seed, lastModifiedTour.getTechnician().getID(), solution);
} else {
throw new IllegalStateException("Could not remove request " + seed);
}
while (remRequests.size() < numReq) {
// Select a new seed
seed = remRequests.get(parameters.getRandomStream().nextInt(0, remRequests.size() - 1));
// Evaluate the relatedness values
Relatedness[] evals = evaluateRelatedRequests(seed, solution, candidates);
// The k-th most related request will be selected
int k = (int) Math.floor(Math.pow(parameters.getRandomStream().nextDouble(), getRandomization())
* candidates.size());
// Select the k-th most related request
Relatedness sel = quickselect(evals, k + 1, candidates);
if (sel == null)
// There are no more request to evaluate
break;
int req = sel.request2;
// Find the tour serving the request and remove it
lastModifiedTour = removeRequest(solution, req);
if (lastModifiedTour != null) {
remRequests.add(req);
candidates.remove(req);
requestRemoved(req, lastModifiedTour.getTechnician().getID(), solution);
} else {
throw new IllegalStateException("Attempting to remove a request that is not in any tour: " + req);
}
}
return new HashSet<Integer>(remRequests);
}
/**
* Returns the <code>k</code> smallest values of an array
*
* @param values
* an array of double
* @param k
* the number of elements to extract
* @param filter
* the indexes to consider
* @return the <code>n</code> biggest elements of <code>values</code> sorted in ascending order
*/
public static <T extends Comparable<? super T>> T quickselect(T[] values, int k, Set<Integer> filter) {
if (values.length == 0 || k == 0)
return null;
if (k == 1) {
// More efficient implementation
return min(values, filter);
}
T[] filteredValues = Arrays.copyOf(values, filter.size());
int j = 0;
for (int i : filter) {
filteredValues[j++] = values[i];
}
return QuickSelect.quickSelect(filteredValues, k, true);
}
/**
* Returns the minimum value of an array
*
* @param values
* an array of value
* @param filter
* the indexes to consider
* @return the maximum of all elements of <code>values</code>
*/
public static <T extends Comparable<? super T>> T min(T[] values, Set<Integer> filter) {
if (values.length == 0)
throw new IllegalArgumentException("Must have at least one value");
T min = null;
for (int i : filter) {
if (values[i] != null && (min == null || values[i].compareTo(min) < 0))
min = values[i];
}
return min;
}
/**
* This method is called at the beginning of the execution of {@link #doDestroy(TRSPSolution, IParameters,
* List<Integer>, int)}. It can be overridden in subclasses to initialize data structures.
*
* @param solution
* the solution that will be destroyed
* @param params
* the parameters of the destroy procedure
* @param remRequests
* the number of requests that will be removed
* @param candidates
* the requests candidate for removal
*/
protected void initialize(TRSPSolution solution, IParameters params, int remRequests, Set<Integer> candidates) {
// Do nothing
}
/**
* This method is called whenever a request is removed from a tour and can be overridden by subclasses, e.g., to
* update data structures.
*
* @param request
* the request that was removed
* @param solution
* the current solution
*/
protected void requestRemoved(int request, int tour, TRSPSolution solution) {
// Do nothing
}
/**
* Evaluation of the candidate set
*
* @param seed
* the seed request
* @param solution
* the current solution
* @param candidates
* the set of candidate requests
* @return an array containing the evaluation of at least all the requests from the <code>candidate</code> set
*/
protected abstract Relatedness[] evaluateRelatedRequests(int seed, TRSPSolution solution, Set<Integer> candidates);
@Override
public abstract DestroyRelated clone();
@Override
public String toString() {
return String.format("%s[p:%s]", getName(), getRandomization());
}
/**
* Store the relatedness value <code>r<sub>ij</sub></code>
*/
protected static class Relatedness extends Evaluation {
final int request2;
/**
* Creates a new <code>Relatedness</code>
*
* @param i
* @param j
* @param rij
*/
public Relatedness(int i, int j, double rij) {
super(i, null, rij);
this.request2 = j;
}
@Override
public String toString() {
return String.format("r%s,%s=%s", request, request2, eval);
}
}
}