mirror of
https://github.com/graphhopper/jsprit.git
synced 2020-01-24 07:45:05 +01:00
refine string removal
This commit is contained in:
parent
142c3beff6
commit
25fe083809
8 changed files with 960 additions and 264 deletions
|
|
@ -82,7 +82,9 @@ public class Jsprit {
|
|||
WORST_BEST("worst_best"),
|
||||
WORST_REGRET("worst_regret"),
|
||||
CLUSTER_BEST("cluster_best"),
|
||||
CLUSTER_REGRET("cluster_regret");
|
||||
CLUSTER_REGRET("cluster_regret"),
|
||||
STRING_BEST("string_best"),
|
||||
STRING_REGRET("string_regret");
|
||||
|
||||
String strategyName;
|
||||
|
||||
|
|
@ -178,6 +180,10 @@ public class Jsprit {
|
|||
defaults.put(Strategy.RADIAL_REGRET.toString(), ".5");
|
||||
defaults.put(Strategy.RANDOM_BEST.toString(), ".5");
|
||||
defaults.put(Strategy.RANDOM_REGRET.toString(), ".5");
|
||||
|
||||
defaults.put(Strategy.STRING_BEST.toString(), ".5");
|
||||
defaults.put(Strategy.STRING_REGRET.toString(), ".5");
|
||||
|
||||
defaults.put(Strategy.WORST_BEST.toString(), "0.");
|
||||
defaults.put(Strategy.WORST_REGRET.toString(), "1.");
|
||||
defaults.put(Strategy.CLUSTER_BEST.toString(), "0.");
|
||||
|
|
@ -472,6 +478,9 @@ public class Jsprit {
|
|||
random)
|
||||
);
|
||||
|
||||
final RuinString stringRuin = new RuinString(vrp, jobNeighborhoods);
|
||||
stringRuin.setRandom(random);
|
||||
|
||||
AbstractInsertionStrategy regret;
|
||||
final ScoringFunction scorer;
|
||||
|
||||
|
|
@ -592,6 +601,11 @@ public class Jsprit {
|
|||
final SearchStrategy clusters_best = new SearchStrategy(Strategy.CLUSTER_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
|
||||
clusters_best.addModule(new RuinAndRecreateModule(Strategy.CLUSTER_BEST.toString(), best, clusters));
|
||||
|
||||
SearchStrategy stringRegret = new SearchStrategy(Strategy.STRING_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction);
|
||||
stringRegret.addModule(new RuinAndRecreateModule(Strategy.STRING_REGRET.toString(), regret, stringRuin));
|
||||
|
||||
SearchStrategy stringBest = new SearchStrategy(Strategy.STRING_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
|
||||
stringBest.addModule(new RuinAndRecreateModule(Strategy.STRING_BEST.toString(), best, stringRuin));
|
||||
|
||||
PrettyAlgorithmBuilder prettyBuilder = PrettyAlgorithmBuilder.newInstance(vrp, fm, stateManager, constraintManager);
|
||||
prettyBuilder.setRandom(random);
|
||||
|
|
@ -605,7 +619,10 @@ public class Jsprit {
|
|||
.withStrategy(worst_best, toDouble(getProperty(Strategy.WORST_BEST.toString())))
|
||||
.withStrategy(worst_regret, toDouble(getProperty(Strategy.WORST_REGRET.toString())))
|
||||
.withStrategy(clusters_regret, toDouble(getProperty(Strategy.CLUSTER_REGRET.toString())))
|
||||
.withStrategy(clusters_best, toDouble(getProperty(Strategy.CLUSTER_BEST.toString())));
|
||||
.withStrategy(clusters_best, toDouble(getProperty(Strategy.CLUSTER_BEST.toString())))
|
||||
.withStrategy(stringBest, toDouble(getProperty(Strategy.STRING_BEST.toString())))
|
||||
.withStrategy(stringRegret, toDouble(getProperty(Strategy.STRING_REGRET.toString())));
|
||||
|
||||
if (getProperty(Parameter.CONSTRUCTION.toString()).equals(Construction.BEST_INSERTION.toString())) {
|
||||
prettyBuilder.constructInitialSolutionWith(best, objectiveFunction);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Licensed to GraphHopper GmbH under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with this work for
|
||||
* additional information regarding copyright ownership.
|
||||
*
|
||||
* GraphHopper GmbH licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.graphhopper.jsprit.core.algorithm.ruin;
|
||||
|
||||
import com.graphhopper.jsprit.core.algorithm.ruin.distance.JobDistance;
|
||||
import com.graphhopper.jsprit.core.problem.AbstractActivity;
|
||||
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
|
||||
import com.graphhopper.jsprit.core.problem.job.Job;
|
||||
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
|
||||
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
|
||||
import com.graphhopper.jsprit.core.util.RandomUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* RuinStrategy that ruins the neighborhood of a randomly selected job. The size and the structure of the neighborhood is defined by
|
||||
* the share of jobs to be removed and the distance between jobs (where distance not necessarily mean Euclidean distance but an arbitrary
|
||||
* measure).
|
||||
*
|
||||
* @author stefan
|
||||
*/
|
||||
public final class RuinString extends AbstractRuinStrategy {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(RuinString.class);
|
||||
|
||||
private VehicleRoutingProblem vrp;
|
||||
|
||||
private JobNeighborhoods jobNeighborhoods;
|
||||
|
||||
private int Kmin = 1;
|
||||
|
||||
private int Kmax = 6;
|
||||
|
||||
private int Lmin = 1;
|
||||
|
||||
private int Lmax = 40;
|
||||
|
||||
/**
|
||||
* Constructs RuinRadial.
|
||||
*
|
||||
* @param vrp
|
||||
* @param jobDistance i.e. a measure to define the distance between two jobs and whether they are located close or distant to eachother
|
||||
* @param Kmin
|
||||
* @param Kmax
|
||||
* @param Lmin
|
||||
* @param Lmax
|
||||
*/
|
||||
public RuinString(VehicleRoutingProblem vrp, JobDistance jobDistance, int Kmin, int Kmax, int Lmin, int Lmax) {
|
||||
super(vrp);
|
||||
this.vrp = vrp;
|
||||
JobNeighborhoodsImplWithCapRestriction jobNeighborhoodsImpl = new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, Kmax * Lmax);
|
||||
jobNeighborhoodsImpl.initialise();
|
||||
jobNeighborhoods = jobNeighborhoodsImpl;
|
||||
this.Kmin = Kmin;
|
||||
this.Kmax = Kmax;
|
||||
this.Lmin = Lmin;
|
||||
this.Lmax = Lmax;
|
||||
logger.debug("initialise {}", this);
|
||||
}
|
||||
|
||||
public RuinString(VehicleRoutingProblem vrp, JobDistance jobDistance) {
|
||||
super(vrp);
|
||||
this.vrp = vrp;
|
||||
JobNeighborhoodsImplWithCapRestriction jobNeighborhoodsImpl = new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, Kmax * Lmax);
|
||||
jobNeighborhoodsImpl.initialise();
|
||||
jobNeighborhoods = jobNeighborhoodsImpl;
|
||||
logger.debug("initialise {}", this);
|
||||
}
|
||||
|
||||
public RuinString(VehicleRoutingProblem vrp, JobNeighborhoods jobNeighborhoods) {
|
||||
super(vrp);
|
||||
this.vrp = vrp;
|
||||
this.jobNeighborhoods = jobNeighborhoods;
|
||||
logger.debug("initialise {}", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[name=splitRuin]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Ruins the collection of vehicleRoutes, i.e. removes a share of jobs. First, it selects a job randomly. Second, it identifies its neighborhood. And finally, it removes
|
||||
* the neighborhood plus the randomly selected job from the number of vehicleRoutes. All removed jobs are then returned as a collection.
|
||||
*/
|
||||
@Override
|
||||
public Collection<Job> ruinRoutes(Collection<VehicleRoute> vehicleRoutes) {
|
||||
if (vehicleRoutes.isEmpty() || vrp.getJobs().isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
int noStrings = Kmin + random.nextInt((Kmax - Kmin));
|
||||
noStrings = Math.min(noStrings, vehicleRoutes.size());
|
||||
Set<Job> unassignedJobs = new HashSet<>();
|
||||
Set<VehicleRoute> ruinedRoutes = new HashSet<>();
|
||||
Job prevJob = RandomUtils.nextJob(vrp.getJobs().values(), random);
|
||||
Iterator<Job> neighborhoodIterator = jobNeighborhoods.getNearestNeighborsIterator(Kmax * Lmax, prevJob);
|
||||
while (neighborhoodIterator.hasNext() && ruinedRoutes.size() <= noStrings) {
|
||||
if (!unassignedJobs.contains(prevJob)) {
|
||||
VehicleRoute route = getRouteOf(prevJob, vehicleRoutes);
|
||||
if (route != null && !ruinedRoutes.contains(route)) {
|
||||
if (random.nextDouble() < .5) {
|
||||
ruinRouteWithStringRuin(route, prevJob, unassignedJobs);
|
||||
} else {
|
||||
ruinRouteWithSplitStringRuin(route, prevJob, unassignedJobs);
|
||||
}
|
||||
ruinedRoutes.add(route);
|
||||
}
|
||||
}
|
||||
prevJob = neighborhoodIterator.next();
|
||||
}
|
||||
return unassignedJobs;
|
||||
}
|
||||
|
||||
private VehicleRoute getRouteOf(Job job, Collection<VehicleRoute> vehicleRoutes) {
|
||||
for (VehicleRoute route : vehicleRoutes) {
|
||||
if (route.getTourActivities().servesJob(job)) return route;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ruinRouteWithSplitStringRuin(VehicleRoute seedRoute, Job prevJob, Set<Job> unassignedJobs) {
|
||||
int noActivities = seedRoute.getActivities().size();
|
||||
int stringLength = Lmin + random.nextInt(Lmax - Lmin);
|
||||
stringLength = Math.min(stringLength, seedRoute.getActivities().size());
|
||||
|
||||
int preservedSubstringLength = StringUtil.determineSubstringLength(stringLength, noActivities, random);
|
||||
|
||||
List<AbstractActivity> acts = vrp.getActivities(prevJob);
|
||||
AbstractActivity randomSeedAct = RandomUtils.nextItem(acts, random);
|
||||
int seedIndex = 0;
|
||||
|
||||
int index = 0;
|
||||
for (TourActivity act : seedRoute.getActivities()) {
|
||||
if (act.getIndex() == randomSeedAct.getIndex()) {
|
||||
seedIndex = index;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
int totalStringLength = stringLength + preservedSubstringLength;
|
||||
List<Integer> stringBounds = StringUtil.getLowerBoundsOfAllStrings(totalStringLength, seedIndex, noActivities);
|
||||
if (stringBounds.isEmpty()) return;
|
||||
int lowerBound = RandomUtils.nextItem(stringBounds, random);
|
||||
|
||||
List<Job> jobs2Remove = new ArrayList<>();
|
||||
int startIndexOfPreservedSubstring = random.nextInt(stringLength);
|
||||
int position = 0;
|
||||
int noStringsInPreservedSubstring = 0;
|
||||
boolean isPreservedSubstring = false;
|
||||
for (int i = lowerBound; i < (lowerBound + totalStringLength); i++) {
|
||||
if (position == startIndexOfPreservedSubstring) {
|
||||
isPreservedSubstring = true;
|
||||
}
|
||||
if (noStringsInPreservedSubstring >= preservedSubstringLength) {
|
||||
isPreservedSubstring = false;
|
||||
}
|
||||
if (!isPreservedSubstring) {
|
||||
TourActivity act = seedRoute.getActivities().get(i);
|
||||
if (act instanceof TourActivity.JobActivity) {
|
||||
Job job = ((TourActivity.JobActivity) act).getJob();
|
||||
if (vrp.getJobs().containsKey(job.getId())) {
|
||||
jobs2Remove.add(job);
|
||||
}
|
||||
}
|
||||
} else noStringsInPreservedSubstring++;
|
||||
position++;
|
||||
}
|
||||
for (Job job : jobs2Remove) {
|
||||
removeJob(job, seedRoute);
|
||||
unassignedJobs.add(job);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void ruinRouteWithStringRuin(VehicleRoute seedRoute, Job prevJob, Set<Job> unassignedJobs) {
|
||||
int stringLength = Lmin + random.nextInt(Lmax - Lmin);
|
||||
stringLength = Math.min(stringLength, seedRoute.getActivities().size());
|
||||
List<AbstractActivity> acts = vrp.getActivities(prevJob);
|
||||
AbstractActivity randomSeedAct = RandomUtils.nextItem(acts, random);
|
||||
int seedIndex = 0;
|
||||
int noActivities = seedRoute.getActivities().size();
|
||||
int index = 0;
|
||||
for (TourActivity act : seedRoute.getActivities()) {
|
||||
if (act.getIndex() == randomSeedAct.getIndex()) {
|
||||
seedIndex = index;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
List<Integer> stringBounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
|
||||
if (stringBounds.isEmpty()) return;
|
||||
int lowerBound = RandomUtils.nextItem(stringBounds, random);
|
||||
List<Job> jobs2Remove = new ArrayList<>();
|
||||
for (int i = lowerBound; i < (lowerBound + stringLength); i++) {
|
||||
TourActivity act = seedRoute.getActivities().get(i);
|
||||
if (act instanceof TourActivity.JobActivity) {
|
||||
Job job = ((TourActivity.JobActivity) act).getJob();
|
||||
if (vrp.getJobs().containsKey(job.getId())) {
|
||||
jobs2Remove.add(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Job job : jobs2Remove) {
|
||||
removeJob(job, seedRoute);
|
||||
unassignedJobs.add(job);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Licensed to GraphHopper GmbH under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with this work for
|
||||
* additional information regarding copyright ownership.
|
||||
*
|
||||
* GraphHopper GmbH licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.graphhopper.jsprit.core.algorithm.ruin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by schroeder on 13/01/17.
|
||||
*/
|
||||
class StringUtil {
|
||||
|
||||
static List<Integer> getLowerBoundsOfAllStrings(int length, int seedIndex, int routeLength) {
|
||||
List<Integer> lowerBounds = new ArrayList<>();
|
||||
for (int i = 1; i <= length; i++) {
|
||||
int lower = seedIndex - (length - i);
|
||||
int upper = seedIndex + (i - 1);
|
||||
if (lower >= 0 && upper < routeLength) {
|
||||
lowerBounds.add(lower);
|
||||
}
|
||||
}
|
||||
return lowerBounds;
|
||||
}
|
||||
|
||||
static int determineSubstringLength(int baseLength, int routeLength, Random random) {
|
||||
if (baseLength == routeLength) return 0;
|
||||
int substringLength = 1;
|
||||
while (baseLength + substringLength < routeLength) {
|
||||
if (random.nextDouble() < 0.01) {
|
||||
return substringLength;
|
||||
} else substringLength++;
|
||||
}
|
||||
return substringLength;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Licensed to GraphHopper GmbH under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with this work for
|
||||
* additional information regarding copyright ownership.
|
||||
*
|
||||
* GraphHopper GmbH licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.graphhopper.jsprit.core.algorithm.ruin;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by schroeder on 13/01/17.
|
||||
*/
|
||||
public class StringUtilTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
int stringLength = 4;
|
||||
int seedIndex = 4;
|
||||
int noActivities = 10;
|
||||
List<Integer> bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
|
||||
Assert.assertEquals(4, bounds.size());
|
||||
Assert.assertEquals(1, (int) bounds.get(0));
|
||||
Assert.assertEquals(2, (int) bounds.get(1));
|
||||
Assert.assertEquals(3, (int) bounds.get(2));
|
||||
Assert.assertEquals(4, (int) bounds.get(3));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2() {
|
||||
int stringLength = 4;
|
||||
int seedIndex = 2;
|
||||
int noActivities = 10;
|
||||
List<Integer> bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
|
||||
Assert.assertEquals(3, bounds.size());
|
||||
Assert.assertEquals(0, (int) bounds.get(0));
|
||||
Assert.assertEquals(1, (int) bounds.get(1));
|
||||
Assert.assertEquals(2, (int) bounds.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3() {
|
||||
int stringLength = 4;
|
||||
int seedIndex = 0;
|
||||
int noActivities = 10;
|
||||
List<Integer> bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
|
||||
Assert.assertEquals(1, bounds.size());
|
||||
Assert.assertEquals(0, (int) bounds.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test4() {
|
||||
int stringLength = 4;
|
||||
int seedIndex = 9;
|
||||
int noActivities = 10;
|
||||
List<Integer> bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
|
||||
Assert.assertEquals(1, bounds.size());
|
||||
Assert.assertEquals(6, (int) bounds.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test5() {
|
||||
int stringLength = 4;
|
||||
int seedIndex = 8;
|
||||
int noActivities = 10;
|
||||
List<Integer> bounds = StringUtil.getLowerBoundsOfAllStrings(stringLength, seedIndex, noActivities);
|
||||
Assert.assertEquals(2, bounds.size());
|
||||
Assert.assertEquals(5, (int) bounds.get(0));
|
||||
Assert.assertEquals(6, (int) bounds.get(1));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue