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

rinde.sim.pdptw.common.DynamicPDPTWProblem Maven / Gradle / Ivy

The newest version!
/**
 * 
 */
package rinde.sim.pdptw.common;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;
import static rinde.sim.core.model.pdp.PDPScenarioEvent.TIME_OUT;

import java.util.List;
import java.util.Map;

import javax.measure.Measure;

import org.apache.commons.math3.random.MersenneTwister;
import org.eclipse.swt.SWT;

import rinde.sim.core.Simulator;
import rinde.sim.core.TickListener;
import rinde.sim.core.TimeLapse;
import rinde.sim.core.model.Model;
import rinde.sim.core.model.pdp.Depot;
import rinde.sim.core.model.pdp.Parcel;
import rinde.sim.core.model.pdp.Vehicle;
import rinde.sim.scenario.ScenarioController;
import rinde.sim.scenario.ScenarioController.UICreator;
import rinde.sim.scenario.TimedEvent;
import rinde.sim.scenario.TimedEventHandler;
import rinde.sim.ui.View;
import rinde.sim.ui.renderers.CanvasRenderer;
import rinde.sim.ui.renderers.PDPModelRenderer;
import rinde.sim.ui.renderers.PlaneRoadModelRenderer;
import rinde.sim.ui.renderers.Renderer;
import rinde.sim.ui.renderers.RoadUserRenderer;
import rinde.sim.ui.renderers.UiSchema;
import rinde.sim.util.spec.Specification;
import rinde.sim.util.spec.Specification.ISpecification;

import com.google.common.collect.ImmutableMap;

// TODO rename to ProblemInstance? or to Problem?
/**
 * A problem instance for the class of problems which is called dynamic
 * pickup-and-delivery problems with time windows, often abbreviated as dynamic
 * PDPTW.
 * 

* A problem instance is an instance which sets up everything related to the * 'problem' which one tries to solve. The idea is that a user only needs to * worry about adding its own solution to this instance. *

* By default this class needs very little customization, it needs to be given a * scenario which it then uses to configure the simulation. Further it is * required to plug your own vehicle in by using * {@link #addCreator(Class, Creator)}. Optionally this method can also be used * to plug in custom parcels and depots. *

* Currently the Gendreau et al. (2006) benchmark is supported. In the future * this class will also support the Fabri & Recht and Pankratz benchmarks. * @author Rinde van Lon */ public class DynamicPDPTWProblem { // TODO create a builder for configuration of problems // TODO a scenario should be an event list AND a pre-configured set of models // describing the complete problem // TODO a StopCondition should be a first class simulator entity // TODO perhaps a UI config should also be bundled easily? // TODO stats system should be more modular (per model?) and hook directly in // the simulator // TODO if there can be some generic way to hook custom agents into the // simulator/scenario, this class can probably be removed /** * A map which contains the default {@link Creator}s. */ protected static final ImmutableMap, Creator> DEFAULT_EVENT_CREATOR_MAP; static { DEFAULT_EVENT_CREATOR_MAP = ImmutableMap.of( (Class) AddParcelEvent.class, new Creator() { @Override public boolean create(Simulator sim, AddParcelEvent event) { return sim.register(new DefaultParcel(event.parcelDTO)); } }, AddDepotEvent.class, new Creator() { @Override public boolean create(Simulator sim, AddDepotEvent event) { return sim.register(new DefaultDepot(event.position)); } }); } /** * Map containing the {@link Creator}s which handle specific * {@link TimedEvent}s. */ protected final Map, Creator> eventCreatorMap; /** * The {@link ScenarioController} which is used to play the scenario. */ protected final ScenarioController controller; /** * The {@link Simulator} which is used for the simulation. */ protected final Simulator simulator; /** * The {@link UICreator} which is used for creating the default UI. */ protected final DefaultUICreator defaultUICreator; /** * The {@link StatsTracker} which is used internally for gathering statistics. */ protected final StatsTracker statsTracker; /** * The {@link StopCondition} which is used as the condition when the * simulation has to stop. */ protected ISpecification stopCondition; /** * Create a new problem instance using the specified scenario. * @param scen The the {@link DynamicPDPTWScenario} which is used in this * problem. * @param randomSeed The random seed which will be passed into the random * number generator in the simulator. * @param models An optional list of models which can be added, with this * option custom models for specific solutions can be added. */ public DynamicPDPTWProblem(final DynamicPDPTWScenario scen, long randomSeed, Model... models) { simulator = new Simulator(new MersenneTwister(randomSeed), Measure.valueOf( scen.getTickSize(), scen.getTimeUnit())); simulator.register(scen.createRoadModel()); simulator.register(scen.createPDPModel()); for (final Model m : models) { simulator.register(m); } eventCreatorMap = newHashMap(); final TimedEventHandler handler = new TimedEventHandler() { @SuppressWarnings("unchecked") @Override public boolean handleTimedEvent(TimedEvent event) { if (eventCreatorMap.containsKey(event.getClass())) { return ((Creator) eventCreatorMap.get(event.getClass())) .create(simulator, event); } else if (DEFAULT_EVENT_CREATOR_MAP.containsKey(event.getClass())) { return ((Creator) DEFAULT_EVENT_CREATOR_MAP.get(event .getClass())).create(simulator, event); } else if (event.getEventType() == TIME_OUT) { return true; } return false; } }; final int ticks = scen.getTimeWindow().end == Long.MAX_VALUE ? -1 : (int) (scen.getTimeWindow().end - scen.getTimeWindow().begin); controller = new ScenarioController(scen, simulator, handler, ticks); statsTracker = new StatsTracker(controller, simulator); stopCondition = scen.getStopCondition(); simulator.addTickListener(new TickListener() { @Override public void tick(TimeLapse timeLapse) {} @Override public void afterTick(TimeLapse timeLapse) { if (stopCondition.isSatisfiedBy(new SimulationInfo(statsTracker .getStatsDTO(), scen))) { simulator.stop(); } } }); defaultUICreator = new DefaultUICreator(this); } /** * @return The statistics of the current simulation. Note that calling this * method while the simulation is not yet finished gives the * statistics that were gathered up until that moment. */ public StatisticsDTO getStatistics() { return statsTracker.getStatsDTO(); } /** * Enables UI using a default visualization. */ public void enableUI() { enableUI(defaultUICreator); } /** * Allows to add an additional {@link CanvasRenderer} to the default UI. * @param r The {@link CanvasRenderer} to add. */ public void addRendererToUI(CanvasRenderer r) { defaultUICreator.addRenderer(r); } /** * Adds a {@link StopCondition} which indicates when the simulation has to * stop. The condition is added in an OR fashion to the predefined stop * condition of the scenario. So after this method is called the simulation * stops if the scenario stop condition is true OR new condition is true. * Subsequent invocations of this method will just add more conditions in the * same way. * @param condition The stop condition to add. */ public void addStopCondition(ISpecification condition) { stopCondition = Specification.of(stopCondition).or(condition).build(); } /** * Enables UI by allowing plugging in a custom {@link UICreator}. * @param creator The creator to use. */ public void enableUI(UICreator creator) { controller.enableUI(creator); } /** * Executes a simulation of the problem. When the simulation is finished (and * this method returns) the statistics of the simulation are returned. * @return The statistics that were gathered during the simulation. */ public StatisticsDTO simulate() { checkState(eventCreatorMap.containsKey(AddVehicleEvent.class), "A creator for AddVehicleEvent is required, use %s.addCreator(..)", this.getClass().getName()); controller.start(); return getStatistics(); } /** * This method exposes the {@link Simulator} that is managed by this problem * instance. Be careful with using it since it is possible to significantly * alter the behavior of the simulation. * @return The simulator. */ public Simulator getSimulator() { return simulator; } /** * Using this method a {@link Creator} instance can be associated with a * certain event. The creator will be called when the event is issued, it is * the responsibility of the {@link Creator} the create the appropriate * response. This method will override a previously existing creator for the * specified event type if applicable. * @param eventType The event type to which the creator will be associated. * @param creator The creator that will be used. * @param The type of the event. */ public void addCreator(Class eventType, Creator creator) { checkArgument( eventType == AddVehicleEvent.class || eventType == AddParcelEvent.class || eventType == AddDepotEvent.class, "A creator can only be added to one of the following classes: AddVehicleEvent, AddParcelEvent, AddDepotEvent."); eventCreatorMap.put(eventType, creator); } /** * Factory for handling a certain type {@link TimedEvent}s. It is the * responsible of this instance to create the appropriate object when an event * occurs. All created objects can be added to the {@link Simulator} by using * {@link Simulator#register(Object)}. * @param The specific subclass of {@link TimedEvent} for which the * creator should create objects. */ public interface Creator { /** * Should add an object to the simulation. * @param sim The simulator to which the objects can be added. * @param event The {@link TimedEvent} instance that contains the event * details. * @return true if the creation and adding of the object was * successful, false otherwise. */ boolean create(Simulator sim, T event); } /** * This class contains default stop conditions which can be used in the * problem. If you want to create your own stop condition you can do it in the * following way: * *

   * StopCondition sc = new StopCondition() {
   *   @Override
   *   public boolean isSatisfiedBy(SimulationInfo context) {
   *     return true; // <- insert your own condition here
   *   }
   * };
   * 
* * StopConditions can be combined into more complex conditions by using * {@link Specification#of(Spec)}. * @author Rinde van Lon */ public abstract static class StopCondition implements ISpecification { /** * The simulation is terminated once the * {@link rinde.sim.core.model.pdp.PDPScenarioEvent#TIME_OUT} event is * dispatched. */ public static final StopCondition TIME_OUT_EVENT = new StopCondition() { @Override public boolean isSatisfiedBy(SimulationInfo context) { return context.stats.simFinish; } }; /** * The simulation is terminated as soon as all the vehicles are back at the * depot, note that this can be before or after the * {@link rinde.sim.core.model.pdp.PDPScenarioEvent#TIME_OUT} event is * dispatched. */ public static final StopCondition VEHICLES_DONE_AND_BACK_AT_DEPOT = new StopCondition() { @Override public boolean isSatisfiedBy(SimulationInfo context) { return context.stats.totalVehicles == context.stats.vehiclesAtDepot && context.stats.movedVehicles > 0 && context.stats.totalParcels == context.stats.totalDeliveries; } }; /** * The simulation is terminated as soon as any tardiness occurs. */ public static final StopCondition ANY_TARDINESS = new StopCondition() { @Override public boolean isSatisfiedBy(SimulationInfo context) { return context.stats.pickupTardiness > 0 || context.stats.deliveryTardiness > 0; } }; } /** * This is an immutable state object which is exposed to {@link StopCondition} * s. * @author Rinde van Lon */ public static class SimulationInfo { /** * The current statistics. */ public final StatisticsDTO stats; /** * The scenario which is playing. */ public final DynamicPDPTWScenario scenario; /** * Instantiate a new instance using statistics and scenario. * @param st Statistics. * @param scen Scenario. */ protected SimulationInfo(StatisticsDTO st, DynamicPDPTWScenario scen) { stats = st; scenario = scen; } } /** * A default {@link UICreator} used for creating a UI for a problem. * @author Rinde van Lon */ public static class DefaultUICreator implements UICreator { /** * A list of renderers. */ protected final List renderers; /** * The speedup that is passed to the gui. */ protected final int speedup; private final DynamicPDPTWProblem problem; /** * Create a GUI for the specified problem. * @param prob The problem to create a GUI for. */ public DefaultUICreator(DynamicPDPTWProblem prob) { this(prob, 1); } /** * Create a GUI for the specified problem with the specified speed. * @param prob The problem to create a GUI for. * @param speed The speed to use. */ public DefaultUICreator(DynamicPDPTWProblem prob, int speed) { checkArgument(speed >= 1, "speed must be a positive integer"); speedup = speed; problem = prob; renderers = newArrayList(); } /** * @return The default road model renderer. */ protected Renderer planeRoadModelRenderer() { return new PlaneRoadModelRenderer(0.05); } /** * @return The default road user renderer. */ protected Renderer roadUserRenderer() { final UiSchema schema = new UiSchema(false); schema.add(Vehicle.class, SWT.COLOR_RED); schema.add(Depot.class, SWT.COLOR_CYAN); schema.add(Parcel.class, SWT.COLOR_BLUE); return new RoadUserRenderer(schema, false); } /** * @return The default pdp model renderer. */ protected Renderer pdpModelRenderer() { return new PDPModelRenderer(false); } /** * Initializes all renderers. */ protected void initRenderers() { renderers.add(planeRoadModelRenderer()); renderers.add(roadUserRenderer()); renderers.add(pdpModelRenderer()); renderers.add(new StatsPanel(problem.statsTracker)); } @Override public void createUI(Simulator sim) { initRenderers(); View.create(sim).with(renderers.toArray(new Renderer[] {})) .setSpeedUp(speedup).show(); } /** * Add a renderer. * @param r The renderer to add. */ public void addRenderer(Renderer r) { renderers.add(r); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy