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

com.github.rinde.rinsim.scenario.generator.ScenarioGenerator 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.generator;

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

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.measure.Measure;
import javax.measure.quantity.Duration;
import javax.measure.quantity.Length;
import javax.measure.quantity.Velocity;
import javax.measure.unit.Unit;

import org.apache.commons.math3.random.RandomGenerator;

import com.github.rinde.rinsim.core.model.Model;
import com.github.rinde.rinsim.core.model.ModelBuilder;
import com.github.rinde.rinsim.core.model.road.ForwardingRoadModel;
import com.github.rinde.rinsim.core.model.road.RoadModel;
import com.github.rinde.rinsim.core.model.road.RoadModelBuilders.PlaneRMB;
import com.github.rinde.rinsim.core.model.road.RoadModels;
import com.github.rinde.rinsim.core.model.time.TimeModel;
import com.github.rinde.rinsim.geom.Point;
import com.github.rinde.rinsim.pdptw.common.AddDepotEvent;
import com.github.rinde.rinsim.pdptw.common.AddVehicleEvent;
import com.github.rinde.rinsim.scenario.Scenario;
import com.github.rinde.rinsim.scenario.Scenario.AbstractBuilder;
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.generator.Depots.DepotGenerator;
import com.github.rinde.rinsim.scenario.generator.Parcels.ParcelGenerator;
import com.github.rinde.rinsim.scenario.generator.Vehicles.VehicleGenerator;
import com.github.rinde.rinsim.util.TimeWindow;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

/**
 * A generator of {@link Scenario}s.
 * @author Rinde van Lon
 */
// TODO rename to Scenarios? or Generators?
public final class ScenarioGenerator {

  // global properties
  final Builder builder;
  final ImmutableSet> modelBuilders;

  private final ParcelGenerator parcelGenerator;
  private final VehicleGenerator vehicleGenerator;
  private final DepotGenerator depotGenerator;

  private final Unit speedUnit;
  private final Unit distanceUnit;
  private final Unit timeUnit;

  ScenarioGenerator(Builder b) {
    builder = b;
    parcelGenerator = b.parcelGenerator;
    vehicleGenerator = b.vehicleGenerator;
    depotGenerator = b.depotGenerator;
    modelBuilders = ImmutableSet.copyOf(builder.modelSuppliers);

    final List> rmBuilders = findBuildersThatBuild(
      modelBuilders, RoadModel.class);
    checkArgument(rmBuilders.size() == 1,
      "Exactly one RoadModel builder must be supplied, found %s builders.",
      rmBuilders.size());
    final ModelBuilder rmb = rmBuilders.get(0);
    PlaneRMB planeBuilder;
    if (rmb instanceof ForwardingRoadModel.Builder) {
      final ModelBuilder delegate = ((ForwardingRoadModel.Builder) rmb)
        .getDelegateModelBuilder();

      checkArgument(delegate instanceof PlaneRMB);
      planeBuilder = (PlaneRMB) delegate;
    } else {
      checkArgument(rmb instanceof PlaneRMB);
      planeBuilder = (PlaneRMB) rmb;
    }
    distanceUnit = planeBuilder.getDistanceUnit();
    speedUnit = planeBuilder.getSpeedUnit();

    final List> tmBuilders = findBuildersThatBuild(
      modelBuilders, TimeModel.class);
    checkArgument(tmBuilders.size() <= 1,
      "At most one TimeModel builder can be specified.");
    if (tmBuilders.isEmpty()) {
      timeUnit = TimeModel.AbstractBuilder.DEFAULT_TIME_UNIT;
    } else {
      timeUnit =
        ((TimeModel.AbstractBuilder) tmBuilders.get(0)).getTimeUnit();
    }
  }

  @SuppressWarnings("unchecked")
  static > List> findBuildersThatBuild(
      Iterable> builders, Class type) {
    final List> foundBuilders = new ArrayList<>();
    for (final ModelBuilder b : builders) {
      if (type.isAssignableFrom(b.getModelType())) {
        foundBuilders.add((ModelBuilder) b);
      }
    }
    return foundBuilders;
  }

  /**
   * @return The speed unit used in generated scenarios.
   */
  public Unit getSpeedUnit() {
    return speedUnit;
  }

  /**
   * @return The distance unit used in generated scenarios.
   */
  public Unit getDistanceUnit() {
    return distanceUnit;
  }

  /**
   * @return The time unit used in generated scenarios.
   */
  public Unit getTimeUnit() {
    return timeUnit;
  }

  /**
   * @return The time window of generated scenarios.
   */
  public TimeWindow getTimeWindow() {
    return builder.getTimeWindow();
  }

  /**
   * @return The minimum position found in generated scenarios.
   */
  public Point getMin() {
    return parcelGenerator.getMin();
  }

  /**
   * @return The maximum position found in generated scenarios.
   */
  public Point getMax() {
    return parcelGenerator.getMax();
  }

  /**
   * @return The {@link ProblemClass} of the generated scenarios.
   */
  public ProblemClass getProblemClass() {
    return builder.problemClass;
  }

  /**
   * Generates a new {@link Scenario} instance.
   * @param rng The random number generator used for drawing random numbers.
   * @param id The id of this specific scenario.
   * @return A new instance.
   */
  // TODO change rng to seed?
  public Scenario generate(RandomGenerator rng, String id) {
    final ImmutableList.Builder b = ImmutableList.builder();
    // depots
    final Iterable depots = depotGenerator.generate(
      rng.nextLong(), parcelGenerator.getCenter());
    b.addAll(depots);

    // vehicles
    final ImmutableList vehicles = vehicleGenerator.generate(
      rng.nextLong(), parcelGenerator.getCenter(),
      builder.getTimeWindow().end());
    b.addAll(vehicles);

    final TravelTimes tm = createTravelTimes(modelBuilders, getTimeUnit(),
      depots, vehicles);

    // parcels
    b.addAll(parcelGenerator.generate(rng.nextLong(), tm,
      builder.getTimeWindow().end()));

    // time out
    b.add(TimeOutEvent.create(builder.getTimeWindow().end()));

    // create
    return Scenario.builder(builder, builder.problemClass)
      .addModels(modelBuilders)
      .addEvents(b.build())
      .instanceId(id)
      .build();
  }

  /**
   * Create a {@link Builder} for constructing {@link ScenarioGenerator}s.
   * @param problemClass The {@link ProblemClass} of the scenarios that will be
   *          generated by the generator under construction.
   * @return The builder.
   */
  public static Builder builder(ProblemClass problemClass) {
    return new Builder(problemClass);
  }

  /**
   * Create a {@link Builder} for constructing {@link ScenarioGenerator}s.
   * @return The builder.
   */
  public static Builder builder() {
    return new Builder(Scenario.DEFAULT_PROBLEM_CLASS);
  }

  /**
   * Creates a {@link TravelTimes} instance based on the specified
   * {@link Scenario}.
   * @param s The scenario.
   * @return The travel times.
   */
  @SuppressWarnings("null")
  public static TravelTimes createTravelTimes(Scenario s) {
    final Iterable depots = FluentIterable.from(s.getEvents())
      .filter(AddDepotEvent.class);
    final Iterable vehicles = FluentIterable.from(
      s.getEvents())
      .filter(AddVehicleEvent.class);

    final List roadModels = newArrayList();

    Unit timeUnit = TimeModel.AbstractBuilder.DEFAULT_TIME_UNIT;
    for (final ModelBuilder mb : s.getModelBuilders()) {
      if (RoadModel.class.isAssignableFrom(mb.getModelType())) {
        roadModels.add((RoadModel) mb.build(null));
      }
      if (TimeModel.class.isAssignableFrom(mb.getModelType())) {
        timeUnit = ((TimeModel.AbstractBuilder) mb).getTimeUnit();
      }
    }
    checkArgument(roadModels.size() == 1);
    return new DefaultTravelTimes(roadModels.get(0), timeUnit, depots,
      vehicles);
  }

  static TravelTimes createTravelTimes(
      Iterable> modelSuppliers,
      Unit tu,
      Iterable depots,
      Iterable vehicles) {
    final RoadModel rm = getRm(modelSuppliers);
    return new DefaultTravelTimes(rm, tu, depots, vehicles);
  }

  @SuppressWarnings("null")
  static RoadModel getRm(
      Iterable> modelSuppliers) {
    for (final ModelBuilder sup : modelSuppliers) {
      if (RoadModel.class.isAssignableFrom(sup.getModelType())) {
        return (RoadModel) sup.build(null);
      }
    }
    throw new IllegalArgumentException("There is no RoadModel supplier in "
      + modelSuppliers + ".");
  }

  /**
   * Builder for creating {@link ScenarioGenerator} instances.
   * @author Rinde van Lon
   */
  public static class Builder extends AbstractBuilder {
    static final ParcelGenerator DEFAULT_PARCEL_GENERATOR = Parcels.builder()
      .build();
    static final VehicleGenerator DEFAULT_VEHICLE_GENERATOR = Vehicles
      .builder().build();
    static final DepotGenerator DEFAULT_DEPOT_GENERATOR = Depots
      .singleCenteredDepot();

    ParcelGenerator parcelGenerator;
    VehicleGenerator vehicleGenerator;
    DepotGenerator depotGenerator;
    final List> modelSuppliers;
    final ProblemClass problemClass;

    Builder(ProblemClass pc) {
      super(Optional.>absent());
      problemClass = pc;
      parcelGenerator = DEFAULT_PARCEL_GENERATOR;
      vehicleGenerator = DEFAULT_VEHICLE_GENERATOR;
      depotGenerator = DEFAULT_DEPOT_GENERATOR;
      modelSuppliers = newArrayList();
    }

    // copying constructor
    Builder(Builder b) {
      super(Optional.>of(b));
      problemClass = b.problemClass;
      parcelGenerator = b.parcelGenerator;
      vehicleGenerator = b.vehicleGenerator;
      depotGenerator = b.depotGenerator;
      modelSuppliers = newArrayList(b.modelSuppliers);
    }

    @Override
    protected Builder self() {
      return this;
    }

    /**
     * Set the {@link VehicleGenerator} to use for adding vehicles to the
     * scenario.
     * @param vg The vehicle generator.
     * @return This, as per the builder pattern.
     */
    public Builder vehicles(VehicleGenerator vg) {
      vehicleGenerator = vg;
      return this;
    }

    /**
     * Set the {@link ParcelGenerator} to use for adding parcels to the
     * scenario.
     * @param pg The parcel generator.
     * @return This, as per the builder pattern.
     */
    public Builder parcels(ParcelGenerator pg) {
      parcelGenerator = pg;
      return this;
    }

    /**
     * Set the {@link DepotGenerator} to use for adding depots to the scenario.
     * @param ds The depot generator.
     * @return This, as per the builder pattern.
     */
    public Builder depots(DepotGenerator ds) {
      depotGenerator = ds;
      return this;
    }

    /**
     * Add a builder of a {@link Model}. The provided model builder will use
     * default values provided by the {@link ScenarioGenerator} instance which
     * is currently being constructed.
     * @param modelBuilder The model builder to add.
     * @return This, as per the builder pattern.
     */
    public Builder addModel(ModelBuilder modelBuilder) {
      modelSuppliers.add(modelBuilder);
      return this;
    }

    /**
     * @return Constructs a new {@link ScenarioGenerator} instance based on this
     *         builder.
     */
    public ScenarioGenerator build() {
      return new ScenarioGenerator(new Builder(this));
    }
  }

  /**
   * Implementations should provide information about travel times in a
   * scenario. The travel times are usually extracted from a {@link RoadModel}.
   * @author Rinde van Lon
   */
  public interface TravelTimes {
    /**
     * Computes the travel time between from and to
     * using the fastest available vehicle.
     * @param from The origin position.
     * @param to The destination position.
     * @return The expected travel time between the two positions.
     */
    long getShortestTravelTime(Point from, Point to);

    /**
     * Computes the travel time between from and the nearest depot
     * using the fastest available vehicle.
     * @param from The origin position.
     * @return The expected travel time between the two positions.
     */
    long getTravelTimeToNearestDepot(Point from);
  }

  static class DefaultTravelTimes implements TravelTimes {
    private final RoadModel roadModel;
    private final Measure vehicleSpeed;
    private final Unit timeUnit;
    private final ImmutableList depotLocations;

    DefaultTravelTimes(RoadModel rm, Unit tu,
        Iterable depots,
        Iterable vehicles) {
      roadModel = rm;

      double max = 0;
      for (final AddVehicleEvent ave : vehicles) {
        max = Math.max(max, ave.getVehicleDTO().getSpeed());
      }
      vehicleSpeed = Measure.valueOf(max, roadModel.getSpeedUnit());

      final ImmutableList.Builder depotBuilder = ImmutableList.builder();
      for (final AddDepotEvent ade : depots) {
        depotBuilder.add(ade.getPosition());
      }
      depotLocations = depotBuilder.build();

      timeUnit = tu;
    }

    @Override
    public long getShortestTravelTime(Point from, Point to) {
      final Iterator path = roadModel.getShortestPathTo(from, to)
        .iterator();

      long travelTime = 0L;
      Point prev = path.next();
      while (path.hasNext()) {
        final Point cur = path.next();
        final Measure distance = Measure.valueOf(
          Point.distance(prev, cur), roadModel.getDistanceUnit());
        travelTime += RoadModels.computeTravelTime(vehicleSpeed, distance,
          timeUnit);
        prev = cur;
      }
      return travelTime;
    }

    @Override
    public long getTravelTimeToNearestDepot(Point from) {
      return getShortestTravelTime(from, findNearestDepot(from));
    }

    private Point findNearestDepot(Point from) {
      final Iterator it = depotLocations.iterator();
      Point nearestDepot = it.next();
      final double dist = Point.distance(from, nearestDepot);
      while (it.hasNext()) {
        final Point cur = it.next();
        final double d = Point.distance(from, cur);
        if (d < dist) {
          nearestDepot = cur;
        }
      }
      return nearestDepot;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy