1
0
Fork 0
mirror of https://github.com/graphhopper/jsprit.git synced 2020-01-24 07:45:05 +01:00

Merge branch 'routebuilder-break-location'

# Conflicts:
#	jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/solution/route/VehicleRoute.java
This commit is contained in:
oblonski 2017-04-26 15:38:08 +02:00
commit 0fb7469d1f
No known key found for this signature in database
GPG key ID: 179DE487285680D1
7 changed files with 711 additions and 265 deletions

View file

@ -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;
@ -119,7 +121,12 @@ public class Jsprit {
FAST_REGRET("regret.fast"),
MAX_TRANSPORT_COSTS("max_transport_costs"),
CONSTRUCTION("construction"),
BREAK_SCHEDULING("break_scheduling");
BREAK_SCHEDULING("break_scheduling"),
STRING_KMIN("string_kmin"),
STRING_KMAX("string_kmax"),
STRING_LMIN("string_lmin"),
STRING_LMAX("string_lmax");
String paraName;
@ -178,10 +185,21 @@ 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(), "0.0");
defaults.put(Strategy.STRING_REGRET.toString(), "0.0");
defaults.put(Parameter.STRING_KMIN.toString(), "1");
defaults.put(Parameter.STRING_KMAX.toString(), "6");
defaults.put(Parameter.STRING_LMIN.toString(), "10");
defaults.put(Parameter.STRING_LMAX.toString(), "30");
defaults.put(Strategy.WORST_BEST.toString(), "0.");
defaults.put(Strategy.WORST_REGRET.toString(), "1.");
defaults.put(Strategy.CLUSTER_BEST.toString(), "0.");
defaults.put(Strategy.CLUSTER_REGRET.toString(), "1.");
defaults.put(Parameter.FIXED_COST_PARAM.toString(), "0.");
defaults.put(Parameter.VEHICLE_SWITCH.toString(), "true");
defaults.put(Parameter.ITERATIONS.toString(), "2000");
@ -472,6 +490,16 @@ public class Jsprit {
random)
);
int kMin = toInteger(properties.getProperty(Parameter.STRING_KMIN.toString()));
int kMax = toInteger(properties.getProperty(Parameter.STRING_KMAX.toString()));
int lMin = toInteger(properties.getProperty(Parameter.STRING_LMIN.toString()));
int lMax = toInteger(properties.getProperty(Parameter.STRING_LMAX.toString()));
final RuinString stringRuin = new RuinString(vrp, jobNeighborhoods);
stringRuin.setNoRoutes(kMin, kMax);
stringRuin.setStringLength(lMin, lMax);
stringRuin.setRandom(random);
AbstractInsertionStrategy regret;
final ScoringFunction scorer;
@ -592,6 +620,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 +638,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 {

View file

@ -0,0 +1,214 @@
/*
* 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.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.*;
/**
* RuinString is adopted from
*
* Technical report 7.11.2016
* A Fresh Ruin & Recreate Implementation for the Capacitated Vehicle Routing Problem
* Jan Christiaens, Greet Vanden Berghe
* KU Leuven, Department of Computer Science, CODeS & iMinds-ITEC
* Gebr. De Smetstraat 1, 9000 Gent, Belgium, jan.christiaens@cs.kuleuven.be, greet.vandenberghe@cs.kuleuven.be
*
* https://lirias.kuleuven.be/bitstream/123456789/556398/1/asb_rr_2016.pdf
*
* @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 = 30;
private int lMax = 60;
public RuinString(VehicleRoutingProblem vrp, JobNeighborhoods jobNeighborhoods) {
super(vrp);
this.vrp = vrp;
this.jobNeighborhoods = jobNeighborhoods;
logger.debug("initialise {}", this);
}
public void setNoRoutes(int kMin, int kMax) {
this.kMin = kMin;
this.kMax = kMax;
}
public void setStringLength(int lMin, int lMax) {
this.lMin = lMin;
this.lMax = lMax;
}
@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);
}
}
}

View file

@ -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;
}
}

View file

@ -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));
}
}