/**
*
*/
package vroom.optimization.online.jmsa.vrp.vrpsd;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import umontreal.iro.lecuyer.probdist.Distribution;
import vroom.common.modeling.dataModel.INodeVisit;
import vroom.common.modeling.dataModel.attributes.IDemand;
import vroom.common.modeling.dataModel.attributes.RequestAttributeKey;
import vroom.common.modeling.dataModel.attributes.StochasticDemand;
import vroom.optimization.online.jmsa.DistinguishedSolutionBase;
import vroom.optimization.online.jmsa.IActualRequest;
import vroom.optimization.online.jmsa.IDistinguishedSolution;
import vroom.optimization.online.jmsa.IScenario;
import vroom.optimization.online.jmsa.components.ComponentManager;
import vroom.optimization.online.jmsa.components.ISolutionBuilderParam;
import vroom.optimization.online.jmsa.utils.MSALogging;
import vroom.optimization.online.jmsa.vrp.VRPActualRequest;
import vroom.optimization.online.jmsa.vrp.VRPScenario;
import vroom.optimization.online.jmsa.vrp.VRPScenarioRoute;
/**
* <code>VRPSDSmartConsensus</code> is an improvement of {@link VRPSDConsensus} that considers alternative requests
* before before returning the customer selected by consensus.
* <p>
* Creation date: Oct 4, 2010 - 5:48:08 PM
*
* @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 VRPSDSmartConsensus extends VRPSDConsensus {
private final double mAlpha = 1.15;
private final double mLambda = 0.05;
private final double mSecondThreshold = 0.2;
public static boolean sImproveDecision = true;
boolean sDepotOnly = false;
public VRPSDSmartConsensus(ComponentManager<?, ?> componentManager) {
super(componentManager);
}
@Override
public IDistinguishedSolution buildDistinguishedPlan(ISolutionBuilderParam param) {
VRPSDConsensus.sRepTripHack = false;
INodeVisit n = getInstance().getShrunkRequest(0);
// If at a depot use the default improved consensus for VRPSD
if (n.isDepot()) {
DistinguishedSolutionBase s = (DistinguishedSolutionBase) super
.buildDistinguishedPlan(param);
return s;
} // Else find the default consensus request and check if a better
// request exists
else {
int nextRequestId = -1;
double eval = 0, bestEval = -1;
Map<Integer, Double> evaluations = new HashMap<Integer, Double>();
for (IScenario s : getComponentManager().getParentMSAProxy().getScenarioPool()) {
s.acquireLock();
IActualRequest req = s.getFirstActualRequest(0);
if (req != null && isRequestFeasible(req)) {
int reqId = req.getID();
eval = updateEvaluation(evaluations, reqId);
if (eval >= bestEval) {
bestEval = eval;
nextRequestId = reqId;
}
}
s.releaseLock();
}
// Next request
VRPActualRequest m = (VRPActualRequest) getComponentManager().getParentMSAProxy()
.getInstance().getNodeVisit(nextRequestId);
// No suitable request was found, force return to the first depot
if (m == null) {
m = new VRPActualRequest(getInstance().getDepotsVisits().iterator().next());
} // Look for a better request
else if (sImproveDecision && (!sDepotOnly || m.isDepot())) {
/*
* Notations:
* n - current node
* m - next node (consensus)
* j - candidate node
* i,k - predecesor/succesor of j in a given scenario
*/
INodeVisit depot = getInstance().getDepotsVisits().iterator().next();
// Cost of arc (n,m)
double c_nm = getInstance().getCost(getInstance().getShrunkRequest(0), m,
getInstance().getFleet().getVehicle());
// Best candidate evaluation
double bestEval2 = Double.POSITIVE_INFINITY;
// Best candidate
VRPActualRequest bestRequest = null;
// Vehicle remaining capacity
double cap = getInstance().getFleet().getVehicle().getCapacity()
- getInstance().getCurrentLoad(0, 0);
// Candidate requests
Set<VRPActualRequest> candidates = new HashSet<VRPActualRequest>(getInstance()
.getPendingRequests());
Iterator<VRPActualRequest> it = candidates.iterator();
double maxScndOccurences = getMSAProxy().getScenarioPool().size()
* mSecondThreshold;
while (it.hasNext()) {
VRPActualRequest r = it.next();
// Check that the request is not the consensus request
boolean valid = r.getID() == nextRequestId;
IDemand dem = r.getParentRequest().getAttribute(RequestAttributeKey.DEMAND);
if (valid && dem instanceof StochasticDemand) {
// Failure evaluation threshold
Distribution dist = ((StochasticDemand) dem).getDistribution(0);
valid = 1 - dist.cdf(cap) < mLambda;
} else if (valid) {
// Feasible demand
valid = dem.getDemand(0) <= cap;
}
if (!valid) {
it.remove();
} else {
// Check if request appear too often is second place
int secondCount = 0;
for (IScenario s : getComponentManager().getParentMSAProxy()
.getScenarioPool()) {
if (((VRPScenario) s).getRoute(0).length() > 3
&& ((VRPScenario) s).getRoute(0).getNodeAt(3).getID() == r
.getID()) {
secondCount++;
}
if (secondCount > maxScndOccurences) {
it.remove();
break;
}
}
}
}
// Consider all unserved requests
for (VRPActualRequest j : candidates) {
Distribution dist = null;
double failProba = 1;
IDemand dem = j.getParentRequest().getAttribute(RequestAttributeKey.DEMAND);
if (dem instanceof StochasticDemand) {
// Failure evaluation threshold
dist = ((StochasticDemand) dem).getDistribution(0);
failProba = 1 - dist.cdf(cap);
} else {
// Feasible demand
failProba = dem.getDemand(0) <= cap ? 0 : 1;
}
// Scenario dependent detour cost
double scenDetourCost = 0;
int count = 0;
// Evaluate all scenarios
for (IScenario s : getComponentManager().getParentMSAProxy().getScenarioPool()) {
VRPScenario scen = (VRPScenario) s;
// Find the candidate route and position
int indexOfJ = -1;
VRPScenarioRoute routeOfJ = null;
for (VRPScenarioRoute route : scen) {
indexOfJ = route.getNodePosition(j);
if (indexOfJ >= 0) {
routeOfJ = route;
break;
}
}
// The candidate was found in this scenario
if (indexOfJ >= 0) {
INodeVisit i = indexOfJ > 0 ? routeOfJ.getNodeAt(indexOfJ - 1) : null;
INodeVisit k = indexOfJ < routeOfJ.length() - 1 ? routeOfJ
.getNodeAt(indexOfJ + 1) : null;
// Expected cost of detour
// -c_ij -c_jk +c_ik
scenDetourCost += -getCost(i, j) - getCost(j, k) + getCost(i, k);
count++;
}
}
// Average scenario detour cost
scenDetourCost /= count;
// Expected detour cost:
// c_nj +c_jm+p_f (c_j0 + c_0j) + scenDetourCost
double detourCost = +getCost(n, j) + getCost(j, m) + 2 * failProba
* getCost(j, depot) + scenDetourCost;
if (detourCost < bestEval2) {
bestEval2 = detourCost;
bestRequest = j;
}
}
if (bestRequest != null && bestEval2 < c_nm * mAlpha) {
MSALogging
.getComponentsLogger()
.info("%s.buildDistinguishedPlan: consensus decision bypassed - cons: %s (%.3f) detour: %s (%.3f)",
getClass().getSimpleName(), m.getID(), c_nm,
bestRequest.getID(), bestEval2);
m = bestRequest;
bestEval = bestEval2;
} else if (bestRequest != null) {
MSALogging
.getComponentsLogger()
.info("%s.buildDistinguishedPlan: alternative request discarded - cons: %s (%.3f) detour: %s (%.3f) - factor: %.3f",
getClass().getSimpleName(), m.getID(), c_nm,
bestRequest.getID(), bestEval2, bestEval2 / c_nm);
}
}
MSALogging.getComponentsLogger().info(
"%s.buildDistinguishedPlan: best request found : %s - score:%s",
getClass().getSimpleName(), m, bestEval);
return new DistinguishedSolutionBase(m);
}
}
public double getCost(INodeVisit i, INodeVisit j) {
if (i == null || j == null) {
return 0;
} else {
return getInstance().getCost(i, j, getInstance().getFleet().getVehicle());
}
}
}