1
0
Fork 0
mirror of https://github.com/graphhopper/jsprit.git synced 2020-01-24 07:45:05 +01:00
This commit is contained in:
oblonski 2014-11-18 19:33:13 +01:00
parent 9c857962d6
commit b702c8015b
2 changed files with 394 additions and 57 deletions

View file

@ -24,14 +24,15 @@ import jsprit.core.algorithm.recreate.listener.BeforeJobInsertionListener;
import jsprit.core.algorithm.recreate.listener.InsertionEndsListener;
import jsprit.core.algorithm.recreate.listener.InsertionStartsListener;
import jsprit.core.algorithm.ruin.listener.RuinListener;
import jsprit.core.problem.AbstractActivity;
import jsprit.core.problem.VehicleRoutingProblem;
import jsprit.core.problem.job.Job;
import jsprit.core.problem.job.Service;
import jsprit.core.problem.job.*;
import jsprit.core.problem.solution.VehicleRoutingProblemSolution;
import jsprit.core.problem.solution.route.VehicleRoute;
import jsprit.core.problem.solution.route.activity.TourActivity;
import jsprit.core.problem.vehicle.Vehicle;
import jsprit.core.problem.vehicle.VehicleImpl;
import jsprit.core.util.Coordinate;
import jsprit.core.util.Solutions;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
@ -43,11 +44,19 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
public class AlgorithmEventRecorder implements RuinListener, IterationStartsListener, InsertionStartsListener, BeforeJobInsertionListener, InsertionEndsListener, AlgorithmEndsListener {
private boolean renderShipments = false;
public static void writeSolution(VehicleRoutingProblem vrp, VehicleRoutingProblemSolution solution, File outfile){
AlgorithmEventRecorder rec = new AlgorithmEventRecorder(vrp,outfile);
rec.initialiseGraph(vrp);
rec.addRoutes(solution.getRoutes());
rec.finish();
}
public static enum RecordPolicy {
RECORD_AND_WRITE
@ -75,7 +84,10 @@ public class AlgorithmEventRecorder implements RuinListener, IterationStartsList
private int currentIteration = 0;
private VehicleRoutingProblem vrp;
public AlgorithmEventRecorder(VehicleRoutingProblem vrp, File outfile) {
this.vrp = vrp;
graph = new MultiGraph("g");
try {
writer = new FileWriter(outfile);
@ -88,6 +100,11 @@ public class AlgorithmEventRecorder implements RuinListener, IterationStartsList
initialiseGraph(vrp);
}
public AlgorithmEventRecorder(VehicleRoutingProblem vrp, File outfile, boolean renderShipments) {
this.renderShipments = renderShipments;
new AlgorithmEventRecorder(vrp,outfile);
}
public void setRecordingRange(int startIteration, int endIteration){
this.start_recording_at = startIteration;
this.end_recording_at = endIteration;
@ -114,21 +131,39 @@ public class AlgorithmEventRecorder implements RuinListener, IterationStartsList
public void ruinStarts(Collection<VehicleRoute> routes) {
if(!record()) return;
fileSink.stepBegins(graph.getId(),0,BEFORE_RUIN_RENDER_SOLUTION);
recordRoutes(routes);
addRoutes(routes);
fileSink.stepBegins(graph.getId(),0,RUIN);
}
private void recordRoutes(Collection<VehicleRoute> routes) {
private void addRoutes(Collection<VehicleRoute> routes) {
for(VehicleRoute route : routes){
String prevNode = makeStartId(route.getVehicle());
for(TourActivity act : route.getActivities()){
String actNode = ((TourActivity.JobActivity)act).getJob().getId();
addEdge(prevNode+"_"+actNode,prevNode,actNode);
prevNode = actNode;
String actNodeId = getNodeId(act);
addEdge(prevNode+"_"+actNodeId,prevNode,actNodeId);
prevNode = actNodeId;
}
if(route.getVehicle().isReturnToDepot()) {
String lastNode = makeEndId(route.getVehicle());
addEdge(prevNode+"_"+lastNode,prevNode,lastNode);
}
}
}
private String getNodeId(TourActivity act) {
String nodeId = null;
if(act instanceof TourActivity.JobActivity){
Job job = ((TourActivity.JobActivity) act).getJob();
if(job instanceof Service){
nodeId = job.getId();
}
else if(job instanceof Shipment){
if(act.getName().equals("pickupShipment")) nodeId = getFromNodeId((Shipment) job);
else nodeId = getToNodeId((Shipment) job);
}
}
return nodeId;
}
private boolean record() {
@ -143,19 +178,99 @@ public class AlgorithmEventRecorder implements RuinListener, IterationStartsList
@Override
public void removed(Job job, VehicleRoute fromRoute) {
if(!record()) return;
if(job instanceof Service) removeService(job, fromRoute);
else if(job instanceof Shipment) removeShipment(job,fromRoute);
}
private void removeShipment(Job job, VehicleRoute fromRoute) {
Shipment shipment = (Shipment)job;
String fromNodeId = getFromNodeId(shipment);
String toNodeId = getToNodeId(shipment);
// removeNodeAndBelongingEdges(fromNodeId,fromRoute);
// removeNodeAndBelongingEdges(toNodeId,fromRoute);
Edge enteringToNode = getEnteringEdge(toNodeId);
if(enteringToNode.getNode0().getId().equals(fromNodeId)){
markRemoved(graph.getNode(fromNodeId));
markRemoved(graph.getNode(toNodeId));
// i -> from -> to -> j: rem(i,from), rem(from,to), rem(to,j), add(i,j)
Edge enteringFromNode = getEnteringEdge(fromNodeId);
removeEdge(enteringFromNode.getId());
removeEdge(enteringToNode.getId());
if(graph.getNode(toNodeId).getLeavingEdgeSet().isEmpty()){
if(fromRoute.getVehicle().isReturnToDepot()) throw new IllegalStateException("leaving edge is missing");
return;
}
Edge leavingToNode = getLeavingEdge(toNodeId);
removeEdge(leavingToNode.getId());
Node from = enteringFromNode.getNode0();
Node to = leavingToNode.getNode1();
if(!fromRoute.getActivities().isEmpty()){
addEdge(makeEdgeId(from,to),from.getId(),to.getId());
}
}
else{
removeNodeAndBelongingEdges(fromNodeId,fromRoute);
removeNodeAndBelongingEdges(toNodeId,fromRoute);
}
}
private Edge getLeavingEdge(String toNodeId) {
Collection<Edge> edges = graph.getNode(toNodeId).getLeavingEdgeSet();
if(edges.size()==1) return edges.iterator().next();
else{
for(Edge e : edges){
if(e.getId().startsWith("shipment")){ continue; }
return e;
}
}
return null;
}
private Edge getEnteringEdge(String toNodeId) {
Collection<Edge> enteringEdges = graph.getNode(toNodeId).getEnteringEdgeSet();
if(enteringEdges.size()==1) return enteringEdges.iterator().next();
else{
for(Edge e : enteringEdges){
if(e.getId().startsWith("shipment")){ continue; }
return e;
}
}
return null;
}
private String getToNodeId(Shipment shipment) {
return shipment.getId() + "_delivery";
}
private String getFromNodeId(Shipment shipment) {
return shipment.getId() + "_pickup";
}
private void removeService(Job job, VehicleRoute fromRoute) {
String nodeId = job.getId();
removeNodeAndBelongingEdges(nodeId, fromRoute);
}
private void removeNodeAndBelongingEdges(String nodeId, VehicleRoute fromRoute) {
Node node = graph.getNode(nodeId);
markRemoved(node);
Edge entering = node.getEnteringEdge(0);
Edge entering = getEnteringEdge(nodeId);
removeEdge(entering.getId());
Edge leaving = node.getLeavingEdge(0);
if(node.getLeavingEdgeSet().isEmpty()){
if(fromRoute.getVehicle().isReturnToDepot()) throw new IllegalStateException("leaving edge is missing");
return;
}
Edge leaving = getLeavingEdge(nodeId);
removeEdge((leaving.getId()));
Node from = entering.getNode0();
Node to = leaving.getNode1();
if(!fromRoute.getActivities().isEmpty()){
addEdge(makeEdgeId(from,to),from.getId(),to.getId());
}
}
private void markRemoved(Node node) {
@ -170,7 +285,11 @@ public class AlgorithmEventRecorder implements RuinListener, IterationStartsList
public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection<VehicleRoutingProblemSolution> solutions) {
VehicleRoutingProblemSolution solution = Solutions.bestOf(solutions);
fileSink.stepBegins(graph.getId(),0,BEFORE_RUIN_RENDER_SOLUTION);
recordRoutes(solution.getRoutes());
addRoutes(solution.getRoutes());
finish();
}
private void finish() {
try {
fileSink.end();
writer.close();
@ -189,10 +308,50 @@ public class AlgorithmEventRecorder implements RuinListener, IterationStartsList
addVehicle(vehicle);
}
for(Job job : problem.getJobs().values()){
Service service = (Service)job;
addService(service);
addJob(job);
}
}
private void addJob(Job job) {
if(job instanceof Service){
Service service = (Service)job;
addNode(service.getId(), service.getCoord());
markService(service);
}
else if(job instanceof Shipment){
Shipment shipment = (Shipment)job;
String fromNodeId = getFromNodeId(shipment);
addNode(fromNodeId, shipment.getPickupCoord());
String toNodeId = getToNodeId(shipment);
addNode(toNodeId,shipment.getDeliveryCoord());
markShipment(shipment);
if(renderShipments) {
Edge e = graph.addEdge("shipment_" + fromNodeId + "_" + toNodeId, fromNodeId, toNodeId, true);
e.addAttribute("ui.class", "shipment");
}
}
}
private void markShipment(Shipment shipment) {
markPickup(getFromNodeId(shipment));
markDelivery(getToNodeId(shipment));
}
private void markService(Service service) {
if(service instanceof Pickup){
markPickup(service.getId());
}
else if(service instanceof Delivery){
markDelivery(service.getId());
}
}
private void markPickup(String id) {
graph.getNode(id).addAttribute("ui.class","pickup");
}
private void markDelivery(String id) {
graph.getNode(id).addAttribute("ui.class","delivery");
}
private void addVehicle(Vehicle vehicle) {
@ -220,32 +379,98 @@ public class AlgorithmEventRecorder implements RuinListener, IterationStartsList
return vehicle.getId() + "_end";
}
private void addService(Service service) {
Node serviceNode = graph.addNode(service.getId());
serviceNode.addAttribute("x", service.getCoord().getX());
serviceNode.addAttribute("y", service.getCoord().getY());
private void addNode(String nodeId, Coordinate nodeCoord) {
Node node = graph.addNode(nodeId);
node.addAttribute("x", nodeCoord.getX());
node.addAttribute("y", nodeCoord.getY());
}
@Override
public void informInsertionEnds(Collection<VehicleRoute> vehicleRoutes) {
if(!record()) return;
fileSink.stepBegins(graph.getId(),0,CLEAR_SOLUTION);
removeRoutes(vehicleRoutes);
}
private void removeRoutes(Collection<VehicleRoute> vehicleRoutes) {
for(VehicleRoute route : vehicleRoutes){
String prevNode = makeStartId(route.getVehicle());
for(TourActivity act : route.getActivities()){
String actNode = ((TourActivity.JobActivity)act).getJob().getId();
String actNode = getNodeId(act);
removeEdge(prevNode + "_" + actNode);
prevNode = actNode;
}
if(route.getVehicle().isReturnToDepot()) {
String lastNode = makeEndId(route.getVehicle());
removeEdge(prevNode + "_" + lastNode);
}
}
}
@Override
public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route) {
if(!record()) return;
markInserted(job);
handleVehicleSwitch(data, route);
insertJob(job, data, route);
}
private void insertJob(Job job, InsertionData data, VehicleRoute route) {
if(job instanceof Service) insertService(job,data,route);
else if(job instanceof Shipment) insertShipment(job,data,route);
}
private void insertShipment(Job job, InsertionData data, VehicleRoute route) {
String fromNodeId = getFromNodeId((Shipment) job);
String toNodeId = getToNodeId((Shipment) job);
insertNode(toNodeId,data.getDeliveryInsertionIndex(),data,route);
List<AbstractActivity> del = vrp.getActivities(job);
VehicleRoute copied = VehicleRoute.copyOf(route);
copied.getTourActivities().addActivity(data.getDeliveryInsertionIndex(),del.get(1));
insertNode(fromNodeId, data.getPickupInsertionIndex(), data, copied);
}
private void insertService(Job job, InsertionData data, VehicleRoute route) {
insertNode(job.getId(), data.getDeliveryInsertionIndex(), data, route);
}
private void insertNode(String nodeId, int insertionIndex, InsertionData data, VehicleRoute route) {
// VehicleRoute copied = VehicleRoute.copyOf(route);
String node_i;
if(isFirst(insertionIndex,route)) {
node_i = makeStartId(data.getSelectedVehicle());
}
else {
TourActivity.JobActivity jobActivity = (TourActivity.JobActivity) route.getActivities().get(insertionIndex - 1);
node_i = getNodeId(jobActivity);
}
String node_k = nodeId;
String edgeId_1 = node_i + "_" + node_k;
String node_j;
if(isLast(insertionIndex,route)) {
node_j = makeEndId(data.getSelectedVehicle());
}
else {
TourActivity.JobActivity jobActivity = (TourActivity.JobActivity) route.getActivities().get(insertionIndex);
node_j = getNodeId(jobActivity);
}
String edgeId_2 = node_k + "_" + node_j;
addEdge(edgeId_1, node_i, node_k);
if(!(isLast(insertionIndex,route) && !data.getSelectedVehicle().isReturnToDepot())) {
addEdge(edgeId_2, node_k, node_j);
if (!route.getActivities().isEmpty()) {
removeEdge(node_i + "_" + node_j);
}
}
}
private void handleVehicleSwitch(InsertionData data, VehicleRoute route) {
boolean vehicleSwitch = false;
if(!(route.getVehicle() instanceof VehicleImpl.NoVehicle)) {
if (!route.getVehicle().getId().equals(data.getSelectedVehicle().getId())) {
@ -258,56 +483,55 @@ public class AlgorithmEventRecorder implements RuinListener, IterationStartsList
String oldEnd = makeEndId(route.getVehicle());
String lastAct = ((TourActivity.JobActivity)route.getActivities().get(route.getActivities().size()-1)).getJob().getId();
removeEdge(oldStart + "_" + firstAct);
if(route.getVehicle().isReturnToDepot()) {
removeEdge(lastAct + "_" + oldEnd);
}
String newStart = makeStartId(data.getSelectedVehicle());
String newEnd = makeEndId(data.getSelectedVehicle());
addEdge(newStart + "_" + firstAct,newStart,firstAct);
if(data.getSelectedVehicle().isReturnToDepot()) {
addEdge(lastAct + "_" + newEnd, lastAct, newEnd);
}
String node_i;
if(isFirst(data,route)) {
node_i = makeStartId(data.getSelectedVehicle());
}
else {
node_i = ((TourActivity.JobActivity)route.getActivities().get(data.getDeliveryInsertionIndex()-1)).getJob().getId();
}
String node_k = job.getId();
String edgeId_1 = node_i + "_" + node_k;
String node_j;
if(isLast(data,route)) {
node_j = makeEndId(data.getSelectedVehicle());
}
else {
node_j = ((TourActivity.JobActivity)route.getActivities().get(data.getDeliveryInsertionIndex())).getJob().getId();
}
String edgeId_2 = node_k + "_" + node_j;
addEdge(edgeId_1, node_i, node_k);
addEdge(edgeId_2, node_k, node_j);
if(!route.getActivities().isEmpty()){
removeEdge(node_i + "_" + node_j);
}
}
private void markInserted(Job job) {
graph.getNode(job.getId()).removeAttribute("ui.class");
if(job instanceof Service){
markService((Service) job);
}
else{
markShipment((Shipment)job);
}
}
private void removeEdge(String edgeId) {
markEdgeRemoved(edgeId);
graph.removeEdge(edgeId);
}
private boolean isFirst(InsertionData data, VehicleRoute route) {
return data.getDeliveryInsertionIndex() == 0;
private void markEdgeRemoved(String edgeId) {
graph.getEdge(edgeId).addAttribute("ui.class","removed");
}
private boolean isLast(InsertionData data, VehicleRoute route) {
return data.getDeliveryInsertionIndex() == route.getActivities().size();
private boolean isFirst(int index, VehicleRoute route) {
return index == 0;
}
private boolean isLast(int index, VehicleRoute route) {
return index == route.getActivities().size();
}
private void addEdge(String edgeId, String fromNode, String toNode) {
graph.addEdge(edgeId,fromNode,toNode,true);
markEdgeInserted(edgeId);
}
private void markEdgeInserted(String edgeId) {
graph.getEdge(edgeId).addAttribute("ui.class","inserted");
graph.getEdge(edgeId).removeAttribute("ui.class");
}
@Override

View file

@ -33,16 +33,95 @@ import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
import org.graphstream.graph.implementations.MultiGraph;
import org.graphstream.stream.Sink;
import org.graphstream.stream.file.FileSinkImages;
import org.graphstream.ui.swingViewer.View;
import org.graphstream.ui.swingViewer.Viewer;
import javax.swing.*;
import java.awt.*;
import java.io.File;
public class GraphStreamViewer {
private static class EmptySink implements Sink {
@Override
public void graphAttributeAdded(String sourceId, long timeId, String attribute, Object value) {
}
@Override
public void graphAttributeChanged(String sourceId, long timeId, String attribute, Object oldValue, Object newValue) {
}
@Override
public void graphAttributeRemoved(String sourceId, long timeId, String attribute) {
}
@Override
public void nodeAttributeAdded(String sourceId, long timeId, String nodeId, String attribute, Object value) {
}
@Override
public void nodeAttributeChanged(String sourceId, long timeId, String nodeId, String attribute, Object oldValue, Object newValue) {
}
@Override
public void nodeAttributeRemoved(String sourceId, long timeId, String nodeId, String attribute) {
}
@Override
public void edgeAttributeAdded(String sourceId, long timeId, String edgeId, String attribute, Object value) {
}
@Override
public void edgeAttributeChanged(String sourceId, long timeId, String edgeId, String attribute, Object oldValue, Object newValue) {
}
@Override
public void edgeAttributeRemoved(String sourceId, long timeId, String edgeId, String attribute) {
}
@Override
public void nodeAdded(String sourceId, long timeId, String nodeId) {
}
@Override
public void nodeRemoved(String sourceId, long timeId, String nodeId) {
}
@Override
public void edgeAdded(String sourceId, long timeId, String edgeId, String fromNodeId, String toNodeId, boolean directed) {
}
@Override
public void edgeRemoved(String sourceId, long timeId, String edgeId) {
}
@Override
public void graphCleared(String sourceId, long timeId) {
}
@Override
public void stepBegins(String sourceId, long timeId, double step) {
}
}
public static Graph createMultiGraph(String name, String style){
Graph g = new MultiGraph(name);
@ -143,6 +222,12 @@ public class GraphStreamViewer {
private double scaling = 1.0;
private boolean createImageByEvent = false;
private File imageDirectory;
Sink fsi = new EmptySink();
public GraphStreamViewer(VehicleRoutingProblem vrp) {
super();
this.vrp = vrp;
@ -159,6 +244,12 @@ public class GraphStreamViewer {
return this;
}
public GraphStreamViewer createImagesByEvent(boolean createImanges, File outDirectory){
createImageByEvent = true;
imageDirectory = outDirectory;
return this;
}
public GraphStreamViewer setRenderDelay(long ms){
this.renderDelay_in_ms=ms;
return this;
@ -204,6 +295,20 @@ public class GraphStreamViewer {
JFrame jframe = createJFrame(view,scaling);
Sink fsi;
if(createImageByEvent){
FileSinkImages.OutputPolicy outputPolicy = FileSinkImages.OutputPolicy.BY_ELEMENT_EVENT;
String prefix = "screenshot_";
FileSinkImages.OutputType type = FileSinkImages.OutputType.PNG;
FileSinkImages.Resolution resolution = FileSinkImages.Resolutions.HD720;
fsi = new FileSinkImages( type, resolution );
((FileSinkImages)fsi).setStyleSheet(STYLESHEET);
((FileSinkImages)fsi).setOutputPolicy(outputPolicy);
((FileSinkImages)fsi).setLayoutPolicy(FileSinkImages.LayoutPolicy.NO_LAYOUT);
((FileSinkImages)fsi).setQuality(FileSinkImages.Quality.HIGH);
((FileSinkImages)fsi).setRenderer(FileSinkImages.RendererType.SCALA);
}
render(g, view);
}
@ -254,8 +359,7 @@ public class GraphStreamViewer {
private void render(Graph g, View view) {
if(center != null){
view.resizeFrame(view.getWidth(), view.getHeight());
view.getCamera().setViewCenter(center.x, center.y, 0);
view.getCamera().setViewPercent(zoomFactor);
alignCamera(view);
}
for(Vehicle vehicle : vrp.getVehicles()){
@ -284,6 +388,15 @@ public class GraphStreamViewer {
}
private void alignCamera(View view) {
view.getCamera().setViewCenter(center.x, center.y, 0);
view.getCamera().setViewPercent(zoomFactor);
if(fsi instanceof FileSinkImages){
((FileSinkImages) fsi).setViewCenter(center.x, center.y);
((FileSinkImages) fsi).setViewPercent(zoomFactor);
}
}
private JLabel createEmptyLabel() {
JLabel emptyLabel1 = new JLabel();
emptyLabel1.setPreferredSize(new Dimension((int)(40*scaling),(int)(25*scaling)));