com.github.rinde.rinsim.examples.experiment.ExperimentExample.java Source code

Java tutorial

Introduction

Here is the source code for com.github.rinde.rinsim.examples.experiment.ExperimentExample.java

Source

/*
 * 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.examples.experiment;

import static com.google.common.base.Preconditions.checkArgument;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.github.rinde.rinsim.core.SimulatorAPI;
import com.github.rinde.rinsim.core.model.pdp.DefaultPDPModel;
import com.github.rinde.rinsim.core.model.pdp.Parcel;
import com.github.rinde.rinsim.core.model.pdp.TimeWindowPolicy.TimeWindowPolicies;
import com.github.rinde.rinsim.core.model.pdp.VehicleDTO;
import com.github.rinde.rinsim.core.model.road.RoadModelBuilders;
import com.github.rinde.rinsim.experiment.Experiment;
import com.github.rinde.rinsim.experiment.Experiment.SimulationResult;
import com.github.rinde.rinsim.experiment.ExperimentResults;
import com.github.rinde.rinsim.experiment.MASConfiguration;
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.pdptw.common.StatsStopConditions;
import com.github.rinde.rinsim.pdptw.common.TimeLinePanel;
import com.github.rinde.rinsim.scenario.Scenario;
import com.github.rinde.rinsim.scenario.StopConditions;
import com.github.rinde.rinsim.scenario.TimeOutEvent;
import com.github.rinde.rinsim.scenario.TimedEventHandler;
import com.github.rinde.rinsim.ui.View;
import com.github.rinde.rinsim.ui.renderers.PDPModelRenderer;
import com.github.rinde.rinsim.ui.renderers.PlaneRoadModelRenderer;
import com.github.rinde.rinsim.util.TimeWindow;
import com.google.common.base.Optional;

/**
 * This example shows how to use the {@link Experiment} class to define and run
 * an experiment. It shows how to construct a {@link Scenario} and
 * {@link MASConfiguration} which are both requirements for an example. The
 * intermediate steps in the example are documented, however, make sure to also
 * read the documentation of each method for extra information about what it
 * does.
 * <p>
 * If this class is run on MacOS it might be necessary to use
 * -XstartOnFirstThread as a VM argument.
 * @author Rinde van Lon
 */
public final class ExperimentExample {
    // some constants used in the experiment
    private static final Point RESOLUTION = new Point(800, 700);
    private static final double VEHICLE_SPEED_KMH = 30d;
    private static final double MAX_VEHICLE_SPEED_KMH = 50d;
    private static final Point MIN_POINT = new Point(0, 0);
    private static final Point MAX_POINT = new Point(8, 4);
    private static final Point DEPOT_LOC = new Point(5, 2);
    private static final Point P1_PICKUP = new Point(1, 2);
    private static final Point P1_DELIVERY = new Point(4, 2);
    private static final Point P2_PICKUP = new Point(1, 1);
    private static final Point P2_DELIVERY = new Point(4, 1);
    private static final Point P3_PICKUP = new Point(1, 3);
    private static final Point P3_DELIVERY = new Point(4, 3);

    private static final long M1 = 60 * 1000L;
    private static final long M4 = 4 * 60 * 1000L;
    private static final long M5 = 5 * 60 * 1000L;
    private static final long M7 = 7 * 60 * 1000L;
    private static final long M10 = 10 * 60 * 1000L;
    private static final long M12 = 12 * 60 * 1000L;
    private static final long M13 = 13 * 60 * 1000L;
    private static final long M18 = 18 * 60 * 1000L;
    private static final long M20 = 20 * 60 * 1000L;
    private static final long M25 = 25 * 60 * 1000L;
    private static final long M30 = 30 * 60 * 1000L;
    private static final long M40 = 40 * 60 * 1000L;
    private static final long M60 = 60 * 60 * 1000L;

    private ExperimentExample() {
    }

    /**
     * It is possible to use the application arguments directly for configuring
     * the experiment. The '-h' or '--help' argument will show the list of
     * options.
     * @param args The arguments supplied to the application.
     */
    public static void main(String[] args) {
        int uiSpeedUp = 1;
        final int index = Arrays.binarySearch(args, "speedup");

        String[] arguments = args;
        if (index >= 0) {
            checkArgument(arguments.length > index + 1,
                    "speedup option requires an integer indicating the speedup.");
            uiSpeedUp = Integer.parseInt(arguments[index + 1]);
            checkArgument(uiSpeedUp > 0, "speedup must be a positive integer.");
            final List<String> list = new ArrayList<>(Arrays.asList(arguments));
            list.remove(index + 1);
            list.remove(index);
            arguments = list.toArray(new String[] {});
        }

        final Optional<ExperimentResults> results;

        // Starts the experiment builder.
        results = Experiment.builder()

                // Adds a configuration to the experiment. A configuration configures an
                // algorithm that is supposed to handle or 'solve' a problem specified by
                // a scenario. A configuration can handle a scenario if it contains an
                // event handler for all events that occur in the scenario. The scenario
                // in this example contains four different events and registers an event
                // handler for each of them.
                .addConfiguration(MASConfiguration.builder()
                        .addEventHandler(AddDepotEvent.class, AddDepotEvent.defaultHandler())
                        .addEventHandler(AddParcelEvent.class, AddParcelEvent.defaultHandler())
                        // There is no default handle for vehicle events, here a non functioning
                        // handler is added, it can be changed to add a custom vehicle to the
                        // simulator.
                        .addEventHandler(AddVehicleEvent.class, CustomVehicleHandler.INSTANCE)
                        .addEventHandler(TimeOutEvent.class, TimeOutEvent.ignoreHandler())

                        // Note: if you multi-agent system requires the aid of a model (e.g.
                        // CommModel) it can be added directly in the configuration. Models that
                        // are only used for the solution side should not be added in the
                        // scenario as they are not part of the problem.
                        .build())

                // Adds the newly constructed scenario to the experiment. Every
                // configuration will be run on every scenario.
                .addScenario(createScenario())

                // The number of repetitions for each simulation. Each repetition will
                // have a unique random seed that is given to the simulator.
                .repeat(2)

                // The master random seed from which all random seeds for the
                // simulations will be drawn.
                .withRandomSeed(0)

                // The number of threads the experiment will use, this allows to run
                // several simulations in parallel. Note that when the GUI is used the
                // number of threads must be set to 1.
                .withThreads(1)

                // We add a post processor to the experiment. A post processor can read
                // the state of the simulator after it has finished. It can be used to
                // gather simulation results. The objects created by the post processor
                // end up in the ExperimentResults object that is returned by the
                // perform(..) method of Experiment.
                .usePostProcessor(new ExamplePostProcessor())

                // Adds the GUI just like it is added to a Simulator object.
                .showGui(View.builder().with(PlaneRoadModelRenderer.builder()).with(PDPModelRenderer.builder())
                        .with(TimeLinePanel.builder()).withResolution((int) RESOLUTION.x, (int) RESOLUTION.y)
                        .withAutoPlay().withAutoClose()
                        // For testing we allow to change the speed up via the args.
                        .withSpeedUp(uiSpeedUp).withTitleAppendix("Experiments example"))

                // Starts the experiment, but first reads the command-line arguments
                // that are specified for this application. By supplying the '-h' option
                // you can see an overview of the supported options.
                .perform(System.out, arguments);

        if (results.isPresent()) {

            for (final SimulationResult sr : results.get().getResults()) {
                // The SimulationResult contains all information about a specific
                // simulation, the result object is the object created by the post
                // processor, a String in this case.
                System.out.println(sr.getSimArgs().getRandomSeed() + " " + sr.getResultObject());
            }
        } else {
            throw new IllegalStateException("Experiment did not complete.");
        }
    }

    /**
     * Defines a simple scenario with one depot, one vehicle and three parcels.
     * Note that a scenario is supposed to only contain problem specific
     * information it should (generally) not make any assumptions about the
     * algorithm(s) that are used to solve the problem.
     * @return A newly constructed scenario.
     */
    static Scenario createScenario() {
        // In essence a scenario is just a list of events. The events must implement
        // the TimedEvent interface. You are free to construct any object as a
        // TimedEvent but keep in mind that implementations should be immutable.
        return Scenario.builder()

                // Adds one depot.
                .addEvent(AddDepotEvent.create(-1, DEPOT_LOC))

                // Adds one vehicle.
                .addEvent(AddVehicleEvent.create(-1, VehicleDTO.builder().speed(VEHICLE_SPEED_KMH).build()))

                // Three add parcel events are added. They are announced at different
                // times and have different time windows.
                .addEvent(AddParcelEvent.create(Parcel.builder(P1_PICKUP, P1_DELIVERY).neededCapacity(0)
                        .orderAnnounceTime(M1).pickupTimeWindow(TimeWindow.create(M1, M20))
                        .deliveryTimeWindow(TimeWindow.create(M4, M30)).buildDTO()))

                .addEvent(AddParcelEvent.create(Parcel.builder(P2_PICKUP, P2_DELIVERY).neededCapacity(0)
                        .orderAnnounceTime(M5).pickupTimeWindow(TimeWindow.create(M10, M25))
                        .deliveryTimeWindow(TimeWindow.create(M20, M40)).buildDTO()))

                .addEvent(AddParcelEvent.create(Parcel.builder(P3_PICKUP, P3_DELIVERY).neededCapacity(0)
                        .orderAnnounceTime(M7).pickupTimeWindow(TimeWindow.create(M12, M18))
                        .deliveryTimeWindow(TimeWindow.create(M13, M60)).buildDTO()))

                // Signals the end of the scenario. Note that it is possible to stop the
                // simulation before or after this event is dispatched, that depends on
                // the stop condition (see below).
                .addEvent(TimeOutEvent.create(M60)).scenarioLength(M60)

                // Adds a plane road model as this is part of the problem
                .addModel(RoadModelBuilders.plane().withMinPoint(MIN_POINT).withMaxPoint(MAX_POINT)
                        .withMaxSpeed(MAX_VEHICLE_SPEED_KMH))

                // Adds the pdp model
                .addModel(DefaultPDPModel.builder().withTimeWindowPolicy(TimeWindowPolicies.TARDY_ALLOWED))

                // The stop condition indicates when the simulator should stop the
                // simulation. Typically this is the moment when all tasks are performed.
                // Custom stop conditions can be created by implementing the StopCondition
                // interface.
                .setStopCondition(StopConditions.or(StatsStopConditions.timeOutEvent(),
                        StatsStopConditions.vehiclesDoneAndBackAtDepot()))
                .build();
    }

    enum CustomVehicleHandler implements TimedEventHandler<AddVehicleEvent> {
        INSTANCE {
            @Override
            public void handleTimedEvent(AddVehicleEvent event, SimulatorAPI sim) {
                // add you own vehicle to the simulator here
            }
        }
    }
}