All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.rinde.rinsim.scenario.gendreau06.Gendreau06Parser Maven / Gradle / Ivy

There is a newer version: 4.4.6
Show newest version
/*
 * Copyright (C) 2011-2016 Rinde van Lon, iMinds-DistriNet, KU Leuven
 *
 * Licensed 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.github.rinde.rinsim.scenario.gendreau06;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.newArrayList;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.RoundingMode;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.github.rinde.rinsim.core.model.pdp.Parcel;
import com.github.rinde.rinsim.core.model.pdp.ParcelDTO;
import com.github.rinde.rinsim.core.model.pdp.VehicleDTO;
import com.github.rinde.rinsim.geom.Point;
import com.github.rinde.rinsim.pdptw.common.AddDepotEvent;
import com.github.rinde.rinsim.pdptw.common.AddParcelEvent;
import com.github.rinde.rinsim.pdptw.common.AddVehicleEvent;
import com.github.rinde.rinsim.scenario.Scenario.ProblemClass;
import com.github.rinde.rinsim.scenario.TimeOutEvent;
import com.github.rinde.rinsim.scenario.TimedEvent;
import com.github.rinde.rinsim.scenario.TimedEvent.TimeComparator;
import com.github.rinde.rinsim.util.TimeWindow;
import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.math.DoubleMath;

/**
 * Parser for files from the Gendreau et al. (2006) data set. The parser allows
 * to customize some of the properties of the scenarios.
 * 

* Format specification: (columns) *

    *
  • 1: request arrival time
  • *
  • 2: pick-up service time
  • *
  • 3 and 4: x and y position for the pick-up
  • *
  • 5 and 6: service window time of the pick-up
  • *
  • 7: delivery service time
  • *
  • 8 and 9:x and y position for the delivery
  • *
  • 10 and 11: service window time of the delivery
  • *
* All times are expressed in seconds. *

* References *

    *
  • [1]. Gendreau, M., Guertin, F., Potvin, J.-Y., and Séguin, R. * Neighborhood search heuristics for a dynamic vehicle dispatching problem with * pick-ups and deliveries. Transportation Research Part C: Emerging * Technologies 14, 3 (2006), 157–174.
  • *
* @author Rinde van Lon */ public final class Gendreau06Parser { private static final String REGEX = ".*req_rapide_(\\d+)_(450|240)_(24|33)"; private static final double TIME_MULTIPLIER = 1000d; private static final int TIME_MULTIPLIER_INTEGER = 1000; private static final int PARCEL_MAGNITUDE = 0; private static final long DEFAULT_TICK_SIZE = 1000L; private static final Point DEPOT_POSITION = new Point(2.0, 2.5); private int numVehicles; private int numParcels; private boolean allowDiversion; private boolean online; private boolean realtime; private long tickSize; private final ImmutableMap.Builder parcelsSuppliers; private Optional> problemClasses; private Gendreau06Parser() { allowDiversion = false; online = true; realtime = false; numVehicles = -1; numParcels = Integer.MAX_VALUE; tickSize = DEFAULT_TICK_SIZE; parcelsSuppliers = ImmutableMap.builder(); problemClasses = Optional.absent(); } /** * @return A {@link Gendreau06Parser}. */ public static Gendreau06Parser parser() { return new Gendreau06Parser(); } /** * Convenience method for parsing a single file. * @param file The file to parse. * @return The scenario as described by the file. */ public static Gendreau06Scenario parse(File file) { return parser().addFile(file).parse().get(0); } /** * @return The {@link Gendreau06Parser} as a {@link Function}. */ public static Function reader() { return Gendreau06Reader.INSTANCE; } /** * Add the specified file to the parser. * @param file The file to add. * @return This, as per the builder pattern. */ public Gendreau06Parser addFile(File file) { checkValidFileName(file.getName()); try { parcelsSuppliers.put(file.getName(), new InputStreamToParcels(new FileInputStream(file))); } catch (final FileNotFoundException e) { throw new IllegalArgumentException(e); } return this; } /** * Add the specified file to the parser. * @param file The file to add. * @return This, as per the builder pattern. */ public Gendreau06Parser addFile(String file) { return addFile(new File(file)); } /** * Add the specified stream to the parser. A file name needs to be specified * for identification of this particular scenario. * @param stream The stream to use for the parsing of the scenario. * @param fileName The file name that identifies the scenario's class and * instance. * @return This, as per the builder pattern. */ public Gendreau06Parser addFile(InputStream stream, String fileName) { checkValidFileName(fileName); parcelsSuppliers.put(fileName, new InputStreamToParcels(stream)); return this; } /** * Add a scenario that is composed using the specified {@link AddParcelEvent} * s. * @param events The events to include. * @param fileName The filename of the scenario. * @return This, as per the builder pattern. */ public Gendreau06Parser addFile(ImmutableList events, String fileName) { checkValidFileName(fileName); parcelsSuppliers.put(fileName, new Parcels(events)); return this; } /** * Adds all Gendreau scenario files in the specified directory. * @param dir The directory to search. * @return This, as per the builder pattern. */ public Gendreau06Parser addDirectory(String dir) { return addDirectory(new File(dir)); } /** * Adds all Gendreau scenario files in the specified directory. * @param dir The directory to search. * @return This, as per the builder pattern. */ public Gendreau06Parser addDirectory(File dir) { checkArgument(dir.isDirectory(), "%s is not a directory.", dir); final File[] files = checkNotNull(dir.listFiles( new FileFilter() { @Override public boolean accept(@Nullable File file) { assert file != null; return isValidFileName(file.getName()); } })); Arrays.sort(files); for (final File f : files) { addFile(f); } return this; } /** * When this method is called all scenarios generated by this parser will * allow vehicle diversion. For more information about the vehicle diversion * concept please consult the class documentation of * {@link com.github.rinde.rinsim.pdptw.common.PDPRoadModel}. * @return This, as per the builder pattern. */ public Gendreau06Parser allowDiversion() { allowDiversion = true; return this; } /** * When this method is called, all scenarios generated by this parser will be * offline scenarios. This means that all parcel events will arrive at time * -1, which means that everything is known beforehand. By * default the parser uses the original event arrival times as defined by the * scenario file. * @return This, as per the builder pattern. */ public Gendreau06Parser offline() { online = false; return this; } public Gendreau06Parser realtime() { realtime = true; return this; } /** * This method allows to override the number of vehicles in the scenarios. For * the default values see {@link GendreauProblemClass}. * @param num The number of vehicles that should be used in the parsed * scenarios. Must be positive. * @return This, as per the builder pattern. */ public Gendreau06Parser setNumVehicles(int num) { checkArgument(num > 0, "The number of vehicles must be positive."); numVehicles = num; return this; } public Gendreau06Parser setNumParcels(int num) { checkArgument(num > 0); numParcels = num; return this; } /** * Change the default tick size of 1000 ms into something else. * @param tick Must be positive. * @return This, as per the builder pattern. */ public Gendreau06Parser setTickSize(long tick) { checkArgument(tick > 0L, "Tick size must be positive."); tickSize = tick; return this; } /** * Filters out any files that are added to this parser which are not of * one of the specified problem classes. * @param classes The problem classes which should be parsed. * @return This, as per the builder pattern. */ public Gendreau06Parser filter(GendreauProblemClass... classes) { problemClasses = Optional.of(ImmutableList.copyOf(classes)); return this; } /** * Parses the files which are added to this parser. In case * {@link #filter(GendreauProblemClass...)} has been called, only files in one * of these problem classes will be parsed. * @return A list of scenarios in order of adding them to the parser. */ public ImmutableList parse() { final ImmutableList.Builder scenarios = ImmutableList .builder(); for (final Entry entry : parcelsSuppliers.build() .entrySet()) { boolean include = false; if (!problemClasses.isPresent()) { include = true; } else { for (final ProblemClass pc : problemClasses.get()) { if (entry.getKey().endsWith(pc.getId())) { include = true; break; } } } if (include) { scenarios.add( parse(entry.getValue(), entry.getKey(), numVehicles, numParcels, tickSize, allowDiversion, online, realtime)); } } return scenarios.build(); } public Function asParseFunction() { final int numV = numVehicles; final int numP = numParcels; final long tick = tickSize; final boolean div = allowDiversion; final boolean onl = online; final boolean rt = realtime; return new Function() { @Nonnull @Override public Gendreau06Scenario apply(@Nullable Path input) { checkArgument(input != null); try { final File file = input.toFile(); return parse( new InputStreamToParcels(new FileInputStream(file)), file.getName(), numV, numP, tick, div, onl, rt); } catch (final FileNotFoundException e) { throw new IllegalArgumentException(); } } }; } static Matcher matcher(String fileName) { return Pattern.compile(REGEX).matcher(fileName); } static boolean isValidFileName(String name) { return Pattern.compile(REGEX).matcher(name).matches(); } static void checkValidFileName(Matcher m, String name) { checkArgument(m.matches(), "The filename must conform to the following regex: %s input was: %s", REGEX, name); } static void checkValidFileName(String name) { checkValidFileName(matcher(name), name); } private static Gendreau06Scenario parse( ParcelsSupplier parcels, String fileName, int numVehicles, int numParcels, final long tickSize, final boolean allowDiversion, boolean online, boolean realtime) { final Matcher m = matcher(fileName); checkValidFileName(m, fileName); final int instanceNumber = Integer.parseInt(m.group(1)); final long minutes = Long.parseLong(m.group(2)); final long totalTime = minutes * 60000L; final long requestsPerHour = Long.parseLong(m.group(3)); final GendreauProblemClass problemClass = GendreauProblemClass.with( minutes, requestsPerHour); final int vehicles = numVehicles == -1 ? problemClass.vehicles : numVehicles; final Point depotPosition = DEPOT_POSITION; final double truckSpeed = 30; final List events = newArrayList(); events.add(AddDepotEvent.create(-1, depotPosition)); for (int i = 0; i < vehicles; i++) { events.add(AddVehicleEvent.create(-1, VehicleDTO.builder() .startPosition(depotPosition) .speed(truckSpeed) .capacity(0) .availabilityTimeWindow(TimeWindow.create(0, totalTime)) .build())); } List parcelList = parcels.get(online); parcelList = parcelList.subList(0, Math.min(parcelList.size(), numParcels)); events.addAll(parcelList); events.add(TimeOutEvent.create(totalTime)); Collections.sort(events, TimeComparator.INSTANCE); return Gendreau06Scenario.create(events, tickSize, problemClass, instanceNumber, allowDiversion, realtime); } static ImmutableList parseParcels(InputStream inputStream, boolean online) { final ImmutableList.Builder listBuilder = ImmutableList .builder(); final BufferedReader reader = new BufferedReader(new InputStreamReader( inputStream, Charsets.UTF_8)); String line; try { while ((line = reader.readLine()) != null) { final Iterator parts = Iterators.forArray(line.split(" ")); final long requestArrivalTime = DoubleMath.roundToLong( Double.parseDouble(parts.next()) * TIME_MULTIPLIER, RoundingMode.HALF_EVEN); // currently filtering out first and last lines of file. Is this ok? if (requestArrivalTime >= 0) { final long pickupServiceTime = Long.parseLong(parts.next()) * TIME_MULTIPLIER_INTEGER; final double pickupX = Double.parseDouble(parts.next()); final double pickupY = Double.parseDouble(parts.next()); final long pickupTimeWindowBegin = DoubleMath.roundToLong( Double.parseDouble(parts.next()) * TIME_MULTIPLIER, RoundingMode.HALF_EVEN); final long pickupTimeWindowEnd = DoubleMath.roundToLong( Double.parseDouble(parts.next()) * TIME_MULTIPLIER, RoundingMode.HALF_EVEN); final long deliveryServiceTime = Long.parseLong(parts.next()) * TIME_MULTIPLIER_INTEGER; final double deliveryX = Double.parseDouble(parts.next()); final double deliveryY = Double.parseDouble(parts.next()); final long deliveryTimeWindowBegin = DoubleMath.roundToLong( Double.parseDouble(parts.next()) * TIME_MULTIPLIER, RoundingMode.HALF_EVEN); final long deliveryTimeWindowEnd = DoubleMath.roundToLong( Double.parseDouble(parts.next()) * TIME_MULTIPLIER, RoundingMode.HALF_EVEN); // when an offline scenario is desired, all times are set to -1 final long arrTime = online ? requestArrivalTime : -1; final ParcelDTO dto = Parcel.builder(new Point(pickupX, pickupY), new Point(deliveryX, deliveryY)).pickupTimeWindow(TimeWindow.create( pickupTimeWindowBegin, pickupTimeWindowEnd)) .deliveryTimeWindow(TimeWindow.create( deliveryTimeWindowBegin, deliveryTimeWindowEnd)) .neededCapacity(PARCEL_MAGNITUDE) .orderAnnounceTime(arrTime) .pickupDuration(pickupServiceTime) .deliveryDuration(deliveryServiceTime) .buildDTO(); listBuilder.add(AddParcelEvent.create(dto)); } } reader.close(); } catch (final IOException e) { throw new IllegalArgumentException(e); } return listBuilder.build(); } enum Gendreau06Reader implements Function { INSTANCE { @Override @Nonnull public Gendreau06Scenario apply(@Nullable Path input) { assert input != null; return Gendreau06Parser.parse(input.toFile()); } @Override public String toString() { return String.format("%s.reader()", Gendreau06Parser.class.getSimpleName()); } }; } interface ParcelsSupplier { /** * Should return an event list. * @param online If false, all events except time-out should have time -1. * @return The list of events. */ ImmutableList get(boolean online); } static class InputStreamToParcels implements ParcelsSupplier { final InputStream inputStream; InputStreamToParcels(InputStream is) { inputStream = is; } @Override public ImmutableList get(boolean online) { return parseParcels(inputStream, online); } } static class Parcels implements ParcelsSupplier { final ImmutableList parcels; Parcels(ImmutableList ps) { parcels = ps; } @Override public ImmutableList get(boolean online) { return parcels; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy