paradiseo/contribution/trunk/combinatorial/routing/cvrptw/src/eoVRPInit.h
legrand 1994652019 added Cvrp-tw stuff instead of vrptw
git-svn-id: svn://scm.gforge.inria.fr/svnroot/paradiseo@836 331e1502-861f-0410-8da2-ba01fb791d7f
2007-12-07 15:50:15 +00:00

606 lines
20 KiB
C++

/*
* Copyright (C) DOLPHIN Project-Team, INRIA Futurs, 2006-2007
* (C) OPAC Team, LIFL, 2002-2007
*
* (c) Antonio LaTorre <atorre@fi.upm.es>, 2007
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/ or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL
* "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided only
* with a limited warranty and the software's author, the holder of the
* economic rights, and the successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated
* with loading, using, modifying and/or developing or reproducing the
* software by the user in light of its specific status of free software,
* that may mean that it is complicated to manipulate, and that also
* therefore means that it is reserved for developers and experienced
* professionals having in-depth computer knowledge. Users are therefore
* encouraged to load and test the software's suitability as regards their
* requirements in conditions enabling the security of their systems and/or
* data to be ensured and, more generally, to use and operate it in the
* same conditions as regards security.
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*
* ParadisEO WebSite : http://paradiseo.gforge.inria.fr
* Contact: paradiseo-help@lists.gforge.inria.fr
*
*/
#ifndef _eoVRPInit_h
#define _eoVRPInit_h
// The base definition of eoInit
#include <eoInit.h>
// Utilities for the VRP-TW problem
#include "eoVRPUtils.h"
/**
* \def ALFA
* Constant used by "selectCheapestClient" method.
* \def BETA
* Constant used by "selectCheapestClient" method.
* \def GAMMA
* Constant used by "selectCheapestClient" method.
*/
#define ALFA 0.7
#define BETA 0.1
#define GAMMA 0.2
/**
* \class eoVRPInit eoVRPInit.h
* \brief Class defining the initializer functor.
* This class initializes an individual of the VRP problem using
* an heuristic initializer.
*/
class eoVRPInit: public eoInit <eoVRP> {
public:
/**
* \brief Default constructor: nothing to do here.
*/
eoVRPInit () {
unsigned sz = eoVRPUtils::clients.size ();
if (sz <= 0) {
std::cerr << "Error: the dataset MUST be read before creating the initializer object." << std::endl;
abort ();
}
mSeedsUsedCount = 0;
for (unsigned i = 0; i < sz; i++)
mSeedsUsed.push_back (false);
}
/**
* \brief Functor member.
* Initializes a genotype using an heuristic initializer.
* \param _gen Generally a genotype that has been default-constructed.
* Whatever it contains will be lost.
*/
void operator () (eoVRP& _gen) {
HeuristicInitialization (_gen);
}
private:
unsigned mSeedsUsedCount; /**< Number of clients already used as seeds. */
std::vector<bool> mSeedsUsed; /**< Vector storing if a client has been used as a seed or not. */
/**
* \brief Heuristic initializer.
* This initializer constructs and individual from routes. Each route is built
* in a constructive way. The first client of each route is selected trying to
* maximize a function depending on its time window and how far it is from the depot.
* We always try to select the hardest clients as seeds. Used seeds are stored
* so that different seeds are selected for different individuals and thus guarantee
* the diversity of the initial population.
* \param _gen The individual to be initialized.
*/
void HeuristicInitialization (eoVRP& _gen) {
// Aux var to override seed used checking
bool seedCheckingOverride = false;
// Erase any previous contents
_gen.clear ();
// Aux vector to store unvisited clients
std::vector<int> unvisited;
// And an index to point to the last unvisited cutomer
int unvisitedIdx = eoVRPUtils::clients.size () - 2;
// Initialization of the unvisited vector with all the clients
for (unsigned i = 1; i < eoVRPUtils::clients.size (); i++)
unvisited.push_back (i);
// Main loop: keep iterating until all clients are visited
while (unvisitedIdx >= 0) {
// Aux var to store the new route
Route route;
createNewRoute (unvisited, unvisitedIdx, seedCheckingOverride, route);
seedCheckingOverride = true;
for (unsigned i = 0; i < route.size (); i++)
_gen.push_back (route [i]);
}
// Invalidates the genotype forcing its re-evaluation
_gen.invalidate ();
}
/**
* \brief Creates a new route.
* Creates a new route selecting the best (hardest) client as seed and then adding
* the cheapest clients until one of the constraints (time window or vehicle's capacity)
* is broken.
* \param _unvisited Vector of unvisited and thus available clients for constructing the new route.
* \param _unvisitedIdx Position of the last univisted client in _unvisited vector.
* \param _seedCheckingOverride If true, it overrides the seed checking mecanism. It must be
* always false for the first route and then true for the following ones.
* This way we will preserve diversity in our initial population as every individual
* will be initialized from a different initial route.
* \param _route The brand new route we have constructed.
* \return True if everything went ok.
*/
bool createNewRoute (std::vector<int>& _unvisited, int& _unvisitedIdx,
bool _seedCheckingOverride, Route& _route ) {
// Selection of seed client for current route
unsigned seed = selectBestClientAsSeed (_unvisited, _unvisitedIdx, _seedCheckingOverride);
// Add the seed client to the route
_route.push_back (_unvisited [seed]);
// Mark the client as already selected as a main seed
// (i.e., as a seed for the first subroute of an individual)
if (!_seedCheckingOverride) {
mSeedsUsed [_unvisited [seed]] = true;
mSeedsUsedCount++;
if (mSeedsUsedCount == mSeedsUsed.size () - 1) {
mSeedsUsedCount = 0;
for (unsigned i = 0; i < mSeedsUsed.size (); i++)
mSeedsUsed [i] = false;
}
}
// Delete the selected client from the unvisited vector
_unvisited [seed] = _unvisited [_unvisitedIdx];
_unvisitedIdx--;
bool feasibleInsert = true;
while (feasibleInsert && _unvisitedIdx >= 0) {
// Aux var to store the position to insert new clients in the route
Route::iterator it;
unsigned next;
if (selectBestInsertion (_unvisited, _unvisitedIdx, _route, next, it)) {
if (it == _route.end ())
_route.insert (_route.begin (), _unvisited [next]);
else
_route.insert (it + 1, _unvisited [next]);
_unvisited [next] = _unvisited [_unvisitedIdx];
_unvisitedIdx--;
}
else
feasibleInsert = false;
}
return true;
}
/**
* \brief Selects the best client and the best position for its insertion in a given route.
* Given a subroute, this method tries to find the best client and the best position for it
* among all the univisited clients.
* \param _unvisited Vector of unvisited and thus available clients for constructing the new route.
* \param _unvisitedIdx Position of the last univisted client in _unvisited vector.
* \param _route The route where we are trying to insert a new client.
* \param _nextClient A return value. The selected client to be inserted.
* \param _it A return value. The position for selected client to be inserted.
* \return True if a new insertion is possible. False otherwise.
*/
bool selectBestInsertion (std::vector<int>& _unvisited, unsigned _unvisitedIdx, Route& _route,
unsigned& _nextClient, Route::iterator& _it ) {
double minCost = 999999999;
bool found = false;
bool insertionPossible = false;
double cost = 0.0;
for (unsigned i = 0; i < _unvisitedIdx; i++) {
insertionPossible = evaluateInsertion (_route, _unvisited [i], -1, cost);
if (insertionPossible && cost < minCost) {
_it = _route.end ();
_nextClient = i;
minCost = cost;
found = true;
}
}
for (Route::iterator it = _route.begin (); it != _route.end (); it++) {
for (unsigned i = 0; i < _unvisitedIdx; i++) {
insertionPossible = evaluateInsertion (_route, _unvisited [i], *it, cost);
if (insertionPossible && cost < minCost) {
_it = it;
_nextClient = i;
minCost = cost;
found = true;
}
}
}
return found;
}
/**
* \brief Evaluates the feasibility and the cost of inserting a new client in a given subroute.
* Given a subroute, this method tries evaluates if it is possible to insert a client in a position.
* It will return the cost of the resulting route if this insertion is possible.
* \param _route The route where we are trying to insert a new client.
* \param _newClient The client we are trying to insert.
* \param _afterClient The position of insertion.
* \param _cost A return value. The cost of inserting the given client at the given position.
* \return True if the new insertion is possible. False otherwise.
*/
bool evaluateInsertion (Route& _route, unsigned _newClient, int _afterClient, double& _cost) {
// First of all, we check for capacity constraint to be satisfied
// before trying to insert the new client in the route
double demand = 0.0;
// Cummulate the demand of all the clients in the current route
for (unsigned i = 0; i < _route.size (); i++)
demand += eoVRPUtils::clients [i].demand;
// And then the demand of the new client
demand += eoVRPUtils::clients [_newClient].demand;
// And finally, check if the cummulated demand exceeds vehicle's capacity
if (demand > VEHICLE_CAPACITY)
return false;
// Now check for insertion position and TW constraints
double readyTime, dueTime, serviceTime;
// If the client must be inserted after client "-1" that means that it
// has to be inserted at the very beginning of the route
if (_afterClient == - 1) {
// We calculate the distante from the depot to the inserted client
_cost = eoVRPUtils::distance (0, _newClient);
// And check that its TW is not exceeded
eoVRPUtils::getTimeWindow (_newClient, readyTime, dueTime, serviceTime);
if (_cost > dueTime)
return false;
// If the vehicle arrives before client's ready time, it has
// to wait until the client is ready
if (_cost < readyTime)
_cost = readyTime;
// Add the service time for the newly inserted client
_cost += serviceTime;
// And the cost of traveling to the next one (the first one in the old route)
_cost = _cost + eoVRPUtils::distance (_newClient, _route [0]);
}
else
// We just need to add the cost of traveling from the depot to the first client
_cost = eoVRPUtils::distance (0, _route [0]);
// Main loop to evaluate the rest of the route (except the last position)
for (unsigned i = 0; i < _route.size () - 1; i++) {
// Check that the TW is not exceeded for the current client
eoVRPUtils::getTimeWindow (_route [i], readyTime, dueTime, serviceTime);
if (_cost > dueTime)
return false;
// If it is not exceeded, we check wether the vehicle arrives before
// the client is ready. If that's the case, it has to wait
if (_cost < readyTime)
_cost = readyTime;
// We add the service time for this client
_cost += serviceTime;
// And now check if we have to insert the new client after the current one
if (_route [i] == _afterClient) {
// If that's the case, we add the cost of traveling from current client
// to the new one
_cost = _cost + eoVRPUtils::distance (_route [i], _newClient);
// And check for its TW to be not exceeded
eoVRPUtils::getTimeWindow (_newClient, readyTime, dueTime, serviceTime);
if (_cost > dueTime)
return false;
// If the vehicle arrives before client's ready time, it has
// to wait until the client is ready
if (_cost < readyTime)
_cost = readyTime;
// Add the service time for the newly inserted client
_cost += serviceTime;
// And the cost of traveling to the next one
_cost = _cost + eoVRPUtils::distance (_newClient, _route [i + 1]);
}
else
// We simply add the cost of traveling to the next client
_cost = _cost + eoVRPUtils::distance (_route [i], _route [i + 1]);
}
// Consider the special case where the new client has
// to be inserted at the end of the route
unsigned last =_route [_route.size () - 1];
// We first check that the TW of the last client in the old route
// has not been exedeed
eoVRPUtils::getTimeWindow (last, readyTime, dueTime, serviceTime);
if (_cost > dueTime)
return false;
// If the vehicle arrives before the client is ready, it waits
if (_cost < readyTime)
_cost = readyTime;
// Now we add its service time
_cost += serviceTime;
// And check if the new client has to be inserted at the end
// of the old route
if (_afterClient == last) {
// In that case, we add the cost of traveling from the last client
// to the one being inserted
_cost = _cost + eoVRPUtils::distance (last, _newClient);
// Check for its TW not being exceeded
eoVRPUtils::getTimeWindow (_newClient, readyTime, dueTime, serviceTime);
if (_cost > dueTime)
return false;
// If the vehicle arrives before the new client is ready, it waits
if (_cost < readyTime)
_cost = readyTime;
// Now we add its service time
_cost += serviceTime;
// And, finally, the time to travel back to the depot
_cost = _cost + eoVRPUtils::distance (_newClient, 0);
}
else
// In this case we just add the cost of traveling back to the depot
_cost = _cost + eoVRPUtils::distance (last, 0);
// Last thing to check is that the vehicle is back on time to the depot
eoVRPUtils::getTimeWindow (0, readyTime, dueTime, serviceTime);
if (_cost > dueTime)
return false;
// If all constraints are satisfied, we return true, and the cost of the
// insertion in the variable "_cost"
return true;
}
/**
* \brief Selects the farthest client as seed for a new route.
* \param _unvisited Vector of unvisited and thus available clients for constructing the new route.
* \param _unvisitedIdx Position of the last univisted client in _unvisited vector.
* \return The position of the client farthest from the depot.
*/
unsigned selectFarthestClientAsSeed (const std::vector<int>& _unvisited, int _unvisitedIdx) {
unsigned maxPos = 0;
double maxDist = eoVRPUtils::distance (0, _unvisited [0]);
for (unsigned i = 1; i <= _unvisitedIdx; i++)
if (eoVRPUtils::distance (0, _unvisited [i]) > maxDist) {
maxPos = i;
maxDist = eoVRPUtils::distance (0, _unvisited [i]);
}
return maxPos;
}
/**
* \brief Selects the cheapest client as seed for a new route.
* \param _unvisited Vector of unvisited and thus available clients for constructing the new route.
* \param _unvisitedIdx Position of the last univisted client in _unvisited vector.
* \param _seedCheckingOverride If true, it overrides the seed checking mecanism.
* \return The position of the cheapest client.
*/
unsigned selectCheapestClient (const std::vector<int>& _unvisited, int _unvisitedIdx, bool _seedCheckingOverride) {
int cheapestPos = -1;
double cheapestCost = 999999999;
for (unsigned i = 0; i <= _unvisitedIdx; i++) {
double cost = (-ALFA * eoVRPUtils::distance (0, _unvisited [i]) ) +
( BETA * eoVRPUtils::clients [_unvisited [i]].dueTime) +
(GAMMA * eoVRPUtils::polarAngle (0, _unvisited [i]) / 360 * eoVRPUtils::distance (0, _unvisited [i]));
if ((cost < cheapestCost || (cost == cheapestCost && rng.flip ())) &&
(!mSeedsUsed [_unvisited [i]] || _seedCheckingOverride ) ) {
cheapestPos = i;
cheapestCost = cost;
}
}
return cheapestPos;
}
/**
* \brief Selects the best (the "hardest" one) client as seed for a new route.
* \param _unvisited Vector of unvisited and thus available clients for constructing the new route.
* \param _unvisitedIdx Position of the last univisted client in _unvisited vector.
* \param _seedCheckingOverride If true, it overrides the seed checking mecanism.
* \return The position of the best client.
*/
unsigned selectBestClientAsSeed (const std::vector<int>& _unvisited, int _unvisitedIdx, bool _seedCheckingOverride) {
int cheapestPos = -1;
double cheapestCost = 999999999;
double alfa, beta;
alfa = rng.uniform ();
beta = rng.uniform ();
for (unsigned i = 0; i <= _unvisitedIdx; i++) {
double cost = (alfa * eoVRPUtils::distance (0, _unvisited [i])) +
(beta * (eoVRPUtils::clients [_unvisited [i]].dueTime - eoVRPUtils::clients [_unvisited [i]].readyTime));
if ((cost < cheapestCost || (cost == cheapestCost && rng.flip ())) &&
(!mSeedsUsed [_unvisited [i]] || _seedCheckingOverride ) ) {
cheapestPos = i;
cheapestCost = cost;
}
}
return cheapestPos;
}
/**
* \brief Random initializer.
* Initializes a genotype using a random initializer.
* @param _gen Generally a genotype that has been default-constructed.
* Whatever it contains will be lost.
*/
void RandomInitializationNoCheck (eoVRP& _gen) {
// Erase any previous contents
_gen.clear ();
// Aux vector to store unvisited clients
std::vector<int> unvisited;
// And an index to point to the last unvisited cutomer
int unvisitedIdx = eoVRPUtils::clients.size () - 2;
// Initialization of the unvisited vector with all the clients
for (unsigned i = 1; i < eoVRPUtils::clients.size (); i++)
unvisited.push_back (i);
while (unvisitedIdx >= 0) {
unsigned n = rng.random (unvisitedIdx);
for (unsigned i = 0; i <= n; i++) {
unsigned pos = rng.random (unvisitedIdx);
_gen.push_back (unvisited [pos]);
unvisited [pos] = unvisited [unvisitedIdx];
unvisitedIdx--;
}
}
}
};
#endif