From 56fe0081ee11eb50c6de191efb2710576742103e Mon Sep 17 00:00:00 2001 From: Plastix Date: Tue, 30 Jan 2018 08:56:19 -0500 Subject: [PATCH 01/18] Add some debugging code --- src/main/java/io/github/plastix/Main.java | 2 +- .../io/github/plastix/SubtourConstraint.java | 21 ++++++- src/main/java/io/github/plastix/Vars.java | 63 ++++++++++++------- 3 files changed, 63 insertions(+), 23 deletions(-) diff --git a/src/main/java/io/github/plastix/Main.java b/src/main/java/io/github/plastix/Main.java index 9010835..3a2f237 100644 --- a/src/main/java/io/github/plastix/Main.java +++ b/src/main/java/io/github/plastix/Main.java @@ -109,7 +109,7 @@ private static void runSolver() throws GRBException { System.out.println("Start position: " + params.getStartLat() + ", " + params.getStartLon() + " (Node " + START_NODE_ID + ")"); -// model.set(GRB.IntParam.LogToConsole, 0); + model.set(GRB.IntParam.LogToConsole, 0); model.optimize(); env.dispose(); diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index f4ebd9f..81710d3 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -28,8 +28,14 @@ protected void callback() { if(where == GRB.CB_MIPSOL) { // Found an integer feasible solution IntHashSet visitedVertices = getReachableVertexSubset(START_NODE_ID); + visitedVertices.remove(START_NODE_ID); int numVerticesInSolution = numVerticesInSolution(); + System.out.println("-- Callback --"); + System.out.println("Solution vertices: " + numVerticesInSolution); + System.out.println("Reachable vertices: " + visitedVertices.size()); + System.out.println("Solution arcs: " + numArcsInSolution()); + // If the number of vertices we can reach from the start is not the number of vertices we // visit in the entire solution, we have a disconnected tour if(visitedVertices.size() != numVerticesInSolution) { @@ -52,6 +58,7 @@ protected void callback() { } double rhs = ((double) sumVertexVisits) / ((double) totalOutgoingEdges); + System.out.println("adding lazy constraint!"); addLazy(subtourConstraint, GRB.GREATER_EQUAL, rhs); } @@ -88,7 +95,7 @@ private IntHashSet getReachableVertexSubset(int startNode) throws GRBException { EdgeIterator iter = explorer.setBaseNode(current); while(iter.next()) { int connectedId = iter.getAdjNode(); - if(getSolution(vars.getArcVar(iter)) > 0) { + if(getSolution(vars.getArcVar(iter)) > 0.5) { stack.addLast(connectedId); } } @@ -98,4 +105,16 @@ private IntHashSet getReachableVertexSubset(int startNode) throws GRBException { return explored; } + + private int numArcsInSolution() throws GRBException { + double[] values = getSolution(vars.getArcVars()); + + int visited = 0; + for(double value : values) { + if(value > 0) { + visited++; + } + } + return visited; + } } diff --git a/src/main/java/io/github/plastix/Vars.java b/src/main/java/io/github/plastix/Vars.java index 4ce29b1..ffb6182 100644 --- a/src/main/java/io/github/plastix/Vars.java +++ b/src/main/java/io/github/plastix/Vars.java @@ -1,5 +1,10 @@ package io.github.plastix; +import com.carrotsearch.hppc.IntIntHashMap; +import com.carrotsearch.hppc.IntIntMap; +import com.carrotsearch.hppc.IntObjectHashMap; +import com.carrotsearch.hppc.IntObjectMap; +import com.carrotsearch.hppc.cursors.IntObjectCursor; import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.storage.Graph; import com.graphhopper.util.EdgeIterator; @@ -16,29 +21,25 @@ public class Vars { private GraphUtils graphUtils; private GRBModel model; - // arcs[arcId][0] = forwardArc - // arcs[arcId][1] = backwardArc - private GRBVar[][] arcs; private GRBVar[] verts; - private int[] arcBaseIds; + private IntObjectMap forwardArcs; + private IntObjectMap backwardArcs; + private IntIntMap arcBaseIds; // Records original "direction" of arc when processed. Vars(Graph graph, GRBModel model, GraphUtils graphUtils) throws GRBException { this.graph = graph; this.model = model; this.graphUtils = graphUtils; + + backwardArcs = new IntObjectHashMap<>(); + forwardArcs = new IntObjectHashMap<>(); + arcBaseIds = new IntIntHashMap(); addVarsToModel(); } private void addVarsToModel() throws GRBException { AllEdgesIterator edges = graph.getAllEdges(); - int numEdges = edges.getMaxId(); int numNodes = graph.getNodes(); - arcBaseIds = new int[numEdges]; - - // Make a decision variable for every arc in our graph - // arcs[i] = 1 if arc is travelled, 0 otherwise - GRBVar[] arcVars = model.addVars(2 * numEdges, GRB.BINARY); - arcs = new GRBVar[numEdges][2]; // Make a variable for every node in the graph // verts[i] = n the number of times vertex i is visited @@ -47,17 +48,18 @@ private void addVarsToModel() throws GRBException { verts = model.addVars(null, null, null, types, null, 0, numNodes); - int i = 0; while(edges.next()) { int edgeId = edges.getEdge(); int baseNode = edges.getBaseNode(); - arcBaseIds[edgeId] = baseNode; + arcBaseIds.put(edgeId, baseNode); - GRBVar forward = arcVars[i++]; - GRBVar backward = arcVars[i++]; + // Make a decision variable for every arc in our graph + // arcs[i] = 1 if arc is travelled, 0 otherwise + GRBVar forward = model.addVar(0, 1, 0, GRB.BINARY, "forward_" + edgeId); + GRBVar backward = model.addVar(0, 1, 0, GRB.BINARY, "backward_" + edgeId); - arcs[edgeId][0] = forward; - arcs[edgeId][1] = backward; + forwardArcs.put(edgeId, forward); + backwardArcs.put(edgeId, backward); if(!graphUtils.isForward(edges)) { forward.set(GRB.DoubleAttr.UB, 0); @@ -69,16 +71,20 @@ private void addVarsToModel() throws GRBException { } } - private int getIndex(EdgeIterator edge) { - return arcBaseIds[edge.getEdge()] == edge.getBaseNode() ? 0 : 1; + private IntObjectMap getMap(EdgeIterator edge) { + return arcBaseIds.get(edge.getEdge()) == edge.getBaseNode() ? forwardArcs : backwardArcs; + } + + private IntObjectMap getComplementMap(EdgeIterator edge) { + return arcBaseIds.get(edge.getEdge()) != edge.getBaseNode() ? forwardArcs : backwardArcs; } public GRBVar getArcVar(EdgeIterator edge) { - return arcs[edge.getEdge()][getIndex(edge)]; + return getMap(edge).get(edge.getEdge()); } public GRBVar getComplementArcVar(EdgeIterator edge) { - return arcs[edge.getEdge()][getIndex(edge) ^ 1]; + return getComplementMap(edge).get(edge.getEdge()); } public GRBVar getVertexVar(int id) { @@ -88,4 +94,19 @@ public GRBVar getVertexVar(int id) { public GRBVar[] getVertexVars() { return verts; } + + public GRBVar[] getArcVars() { + GRBVar[] result = new GRBVar[forwardArcs.size() * 2]; + + int i = 0; + for(IntObjectCursor forwardArc : forwardArcs) { + result[i++] = forwardArc.value; + } + + for(IntObjectCursor backwardArc : backwardArcs) { + result[i++] = backwardArc.value; + } + + return result; + } } From 1b5f97f89724caa9aa5c48fbce7ccee24a5d54d5 Mon Sep 17 00:00:00 2001 From: Plastix Date: Tue, 30 Jan 2018 19:33:36 -0500 Subject: [PATCH 02/18] Get param property resource instead of hardcoding path --- src/main/java/io/github/plastix/Params.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/github/plastix/Params.java b/src/main/java/io/github/plastix/Params.java index 7929d48..30ec8e4 100644 --- a/src/main/java/io/github/plastix/Params.java +++ b/src/main/java/io/github/plastix/Params.java @@ -19,7 +19,8 @@ public void loadParams() { Properties properties = new Properties(); InputStream inputStream; try { - inputStream = new FileInputStream("src/main/resources/params.properties"); + String paramPath = getClass().getResource("/params.properties").getPath(); + inputStream = new FileInputStream(paramPath); properties.load(inputStream); } catch(FileNotFoundException e) { System.out.println("No params file!"); From 88dceb4d67d8d34b502964f67a8b8d1001b7b023 Mon Sep 17 00:00:00 2001 From: Plastix Date: Wed, 31 Jan 2018 07:53:18 -0500 Subject: [PATCH 03/18] Add simple graph unit test Still need to debug and finish writing the test. --- build.gradle | 4 + .../java/io/github/plastix/Constraints.java | 104 +++++++++++++++++ .../java/io/github/plastix/GraphUtils.java | 26 +---- src/main/java/io/github/plastix/Main.java | 104 ++++------------- src/main/java/io/github/plastix/Vars.java | 5 +- src/test/java/SimpleGraphTest.java | 110 ++++++++++++++++++ 6 files changed, 248 insertions(+), 105 deletions(-) create mode 100644 src/main/java/io/github/plastix/Constraints.java create mode 100644 src/test/java/SimpleGraphTest.java diff --git a/build.gradle b/build.gradle index 9041ef4..dcb746e 100644 --- a/build.gradle +++ b/build.gradle @@ -16,4 +16,8 @@ dependencies { compile fileTree(dir: System.getenv('GUROBI_HOME') + '/lib', include: '*.jar') testCompile group: 'junit', name: 'junit', version: '4.12' compile group: 'com.graphhopper', name: 'graphhopper-reader-osm', version: '0.9.0' +} + +test { + testLogging.showStandardStreams = true } \ No newline at end of file diff --git a/src/main/java/io/github/plastix/Constraints.java b/src/main/java/io/github/plastix/Constraints.java new file mode 100644 index 0000000..2c162d3 --- /dev/null +++ b/src/main/java/io/github/plastix/Constraints.java @@ -0,0 +1,104 @@ +package io.github.plastix; + +import com.carrotsearch.hppc.IntHashSet; +import com.graphhopper.routing.util.AllEdgesIterator; +import com.graphhopper.storage.Graph; +import com.graphhopper.util.EdgeIterator; +import gurobi.*; + +public class Constraints { + + private final double MAX_COST; + private Graph graph; + private GRBModel model; + private GraphUtils graphUtils; + private int START_NODE_ID; + + public Constraints(Graph graph, GRBModel model, GraphUtils graphUtils, int START_NODE_ID, double maxCost) { + this.graph = graph; + this.model = model; + this.graphUtils = graphUtils; + this.START_NODE_ID = START_NODE_ID; + this.MAX_COST = maxCost; + } + + public void setupConstraints() throws GRBException { + Vars vars = new Vars(graph, model, graphUtils); + vars.addVarsToModel(); + + // (1a) + // Objective maximizes total collected score of all roads + GRBLinExpr objective = new GRBLinExpr(); + + // (1b) + // Limit length of path + GRBLinExpr maxCost = new GRBLinExpr(); + + AllEdgesIterator edges = graph.getAllEdges(); + while(edges.next()) { + double edgeScore = graphUtils.getArcScore(edges); + double edgeDist = edges.getDistance(); + + GRBVar forward = vars.getArcVar(edges); + GRBVar backward = vars.getComplementArcVar(edges); + + objective.addTerm(edgeScore, forward); + objective.addTerm(edgeScore, backward); + maxCost.addTerm(edgeDist, forward); + maxCost.addTerm(edgeDist, backward); + + // (1j) + GRBLinExpr arcConstraint = new GRBLinExpr(); + arcConstraint.addTerm(1, forward); + arcConstraint.addTerm(1, backward); + model.addConstr(arcConstraint, GRB.LESS_EQUAL, 1, "arc_constraint"); + } + + model.setObjective(objective, GRB.MAXIMIZE); + model.addConstr(maxCost, GRB.LESS_EQUAL, MAX_COST, "max_cost"); + + int numNodes = graph.getNodes(); + for(int i = 0; i < numNodes; i++) { + // (1d) + GRBLinExpr edgeCounts = new GRBLinExpr(); + IntHashSet incomingIds = new IntHashSet(); + EdgeIterator incoming = graphUtils.incomingEdges(i); + while(incoming.next()) { + incomingIds.add(incoming.getEdge()); + edgeCounts.addTerm(1, vars.getArcVar(incoming)); + } + + EdgeIterator outgoing = graphUtils.outgoingEdges(i); + while(outgoing.next()) { + GRBVar arc = vars.getArcVar(outgoing); + // Check if we already recorded it as an incoming edge + if(incomingIds.contains(outgoing.getEdge())) { + edgeCounts.remove(arc); + } else { + edgeCounts.addTerm(-1, arc); + } + } + + model.addConstr(edgeCounts, GRB.EQUAL, 0, "edge_counts"); + + // (1e) + GRBLinExpr vertexVisits = new GRBLinExpr(); + outgoing = graphUtils.outgoingEdges(i); + while(outgoing.next()) { + vertexVisits.addTerm(1, vars.getArcVar(outgoing)); + } + vertexVisits.addTerm(-1, vars.getVertexVar(i)); + model.addConstr(vertexVisits, GRB.EQUAL, 0, "vertex_visits"); + } + + // (1h)/(1i) + // Start vertex must be visited exactly once + GRBVar startNode = vars.getVertexVar(START_NODE_ID); + startNode.set(GRB.DoubleAttr.LB, 1); + startNode.set(GRB.DoubleAttr.UB, 1); + + // Must set LazyConstraints parameter when using lazy constraints + model.set(GRB.IntParam.LazyConstraints, 1); + model.setCallback(new SubtourConstraint(vars, START_NODE_ID, graphUtils)); + } +} diff --git a/src/main/java/io/github/plastix/GraphUtils.java b/src/main/java/io/github/plastix/GraphUtils.java index be5f2e1..6bb9897 100644 --- a/src/main/java/io/github/plastix/GraphUtils.java +++ b/src/main/java/io/github/plastix/GraphUtils.java @@ -1,29 +1,23 @@ package io.github.plastix; import com.graphhopper.routing.util.DefaultEdgeFilter; -import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; -import com.graphhopper.storage.index.LocationIndex; -import com.graphhopper.storage.index.QueryResult; import com.graphhopper.util.EdgeExplorer; import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.EdgeIteratorState; public class GraphUtils { - private Params params; private Graph graph; - private LocationIndex locationIndex; private FlagEncoder flagEncoder; - private BikePriorityWeighting weighting; + private Weighting weighting; - GraphUtils(Graph graph, LocationIndex locationIndex, EncodingManager encodingManager, Params params) { + public GraphUtils(Graph graph, FlagEncoder flagEncoder, Weighting weighting) { this.graph = graph; - this.locationIndex = locationIndex; - this.flagEncoder = encodingManager.getEncoder(params.getVehicle()); - this.params = params; - weighting = new BikePriorityWeighting(flagEncoder); + this.flagEncoder = flagEncoder; + this.weighting = weighting; } public EdgeIterator outgoingEdges(int node) { @@ -50,16 +44,6 @@ public double getArcScore(EdgeIteratorState edge) { return weighting.calcWeight(edge, false, edge.getEdge()); } - public int getStartNode() { - QueryResult result = locationIndex.findClosest(params.getStartLat(), params.getStartLon(), - new DefaultEdgeFilter(flagEncoder)); - if(!result.isValid()) { - throw new RuntimeException("Unable to find node at start lat/lon!"); - } - return result.getClosestNode(); - - } - public boolean isForward(EdgeIteratorState edge) { return edge.isForward(flagEncoder); } diff --git a/src/main/java/io/github/plastix/Main.java b/src/main/java/io/github/plastix/Main.java index 3a2f237..5975d19 100644 --- a/src/main/java/io/github/plastix/Main.java +++ b/src/main/java/io/github/plastix/Main.java @@ -1,13 +1,18 @@ package io.github.plastix; -import com.carrotsearch.hppc.IntHashSet; import com.graphhopper.GraphHopper; import com.graphhopper.reader.osm.GraphHopperOSM; import com.graphhopper.routing.util.AllEdgesIterator; +import com.graphhopper.routing.util.DefaultEdgeFilter; import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; -import com.graphhopper.util.EdgeIterator; -import gurobi.*; +import com.graphhopper.storage.index.QueryResult; +import gurobi.GRB; +import gurobi.GRBEnv; +import gurobi.GRBException; +import gurobi.GRBModel; public class Main { @@ -26,82 +31,8 @@ public class Main { private static void setupSolver() throws GRBException { env = new GRBEnv("osm.log"); model = new GRBModel(env); - Vars vars = new Vars(graph, model, graphUtils); - - // (1a) - // Objective maximizes total collected score of all roads - GRBLinExpr objective = new GRBLinExpr(); - - // (1b) - // Limit length of path - GRBLinExpr maxCost = new GRBLinExpr(); - - AllEdgesIterator edges = graph.getAllEdges(); - while(edges.next()) { - double edgeScore = graphUtils.getArcScore(edges); - double edgeDist = edges.getDistance(); - - GRBVar forward = vars.getArcVar(edges); - GRBVar backward = vars.getComplementArcVar(edges); - - objective.addTerm(edgeScore, forward); - objective.addTerm(edgeScore, backward); - maxCost.addTerm(edgeDist, forward); - maxCost.addTerm(edgeDist, backward); - - // (1j) - GRBLinExpr arcConstraint = new GRBLinExpr(); - arcConstraint.addTerm(1, forward); - arcConstraint.addTerm(1, backward); - model.addConstr(arcConstraint, GRB.LESS_EQUAL, 1, "arc_constraint"); - } - - model.setObjective(objective, GRB.MAXIMIZE); - model.addConstr(maxCost, GRB.LESS_EQUAL, params.getMaxCost(), "max_cost"); - - int numNodes = graph.getNodes(); - for(int i = 0; i < numNodes; i++) { - // (1d) - GRBLinExpr edgeCounts = new GRBLinExpr(); - IntHashSet incomingIds = new IntHashSet(); - EdgeIterator incoming = graphUtils.incomingEdges(i); - while(incoming.next()) { - incomingIds.add(incoming.getEdge()); - edgeCounts.addTerm(1, vars.getArcVar(incoming)); - } - - EdgeIterator outgoing = graphUtils.outgoingEdges(i); - while(outgoing.next()) { - GRBVar arc = vars.getArcVar(outgoing); - // Check if we already recorded it as an incoming edge - if(incomingIds.contains(outgoing.getEdge())) { - edgeCounts.remove(arc); - } else { - edgeCounts.addTerm(-1, arc); - } - } - - model.addConstr(edgeCounts, GRB.EQUAL, 0, "edge_counts"); - - // (1e) - GRBLinExpr vertexVisits = new GRBLinExpr(); - outgoing = graphUtils.outgoingEdges(i); - while(outgoing.next()) { - vertexVisits.addTerm(1, vars.getArcVar(outgoing)); - } - vertexVisits.addTerm(-1, vars.getVertexVar(i)); - model.addConstr(vertexVisits, GRB.EQUAL, 0, "vertex_visits"); - } - - // (1h)/(1i) - // Start vertex must be visited exactly once - GRBVar startNode = vars.getVertexVar(START_NODE_ID); - startNode.set(GRB.DoubleAttr.LB, 1); - startNode.set(GRB.DoubleAttr.UB, 1); - - // Must set LazyConstraints parameter when using lazy constraints - model.set(GRB.IntParam.LazyConstraints, 1); - model.setCallback(new SubtourConstraint(vars, START_NODE_ID, graphUtils)); + Constraints constraints = new Constraints(graph, model, graphUtils, START_NODE_ID, params.getMaxCost()); + constraints.setupConstraints(); } private static void runSolver() throws GRBException { @@ -129,9 +60,11 @@ private static void loadOSM() { hopper.setCHEnabled(false); hopper.importOrLoad(); + FlagEncoder flagEncoder = encodingManager.getEncoder(params.getVehicle()); + Weighting weighting = new BikePriorityWeighting(flagEncoder); graph = hopper.getGraphHopperStorage().getBaseGraph(); - graphUtils = new GraphUtils(graph, hopper.getLocationIndex(), encodingManager, params); - START_NODE_ID = graphUtils.getStartNode(); + graphUtils = new GraphUtils(graph, flagEncoder, weighting); + START_NODE_ID = getStartNode(flagEncoder); AllEdgesIterator edges = graph.getAllEdges(); int nonTraversable = 0; @@ -146,6 +79,15 @@ private static void loadOSM() { graph.getAllEdges().getMaxId(), graph.getNodes(), nonTraversable, oneWay)); } + private static int getStartNode(FlagEncoder flagEncoder) { + QueryResult result = hopper.getLocationIndex().findClosest(params.getStartLat(), params.getStartLon(), + new DefaultEdgeFilter(flagEncoder)); + if(!result.isValid()) { + throw new RuntimeException("Unable to find node at start lat/lon!"); + } + return result.getClosestNode(); + } + public static void main(String[] args) { params = new Params(); params.loadParams(); diff --git a/src/main/java/io/github/plastix/Vars.java b/src/main/java/io/github/plastix/Vars.java index ffb6182..8d169f4 100644 --- a/src/main/java/io/github/plastix/Vars.java +++ b/src/main/java/io/github/plastix/Vars.java @@ -26,7 +26,7 @@ public class Vars { private IntObjectMap backwardArcs; private IntIntMap arcBaseIds; // Records original "direction" of arc when processed. - Vars(Graph graph, GRBModel model, GraphUtils graphUtils) throws GRBException { + Vars(Graph graph, GRBModel model, GraphUtils graphUtils) { this.graph = graph; this.model = model; this.graphUtils = graphUtils; @@ -34,10 +34,9 @@ public class Vars { backwardArcs = new IntObjectHashMap<>(); forwardArcs = new IntObjectHashMap<>(); arcBaseIds = new IntIntHashMap(); - addVarsToModel(); } - private void addVarsToModel() throws GRBException { + public void addVarsToModel() throws GRBException { AllEdgesIterator edges = graph.getAllEdges(); int numNodes = graph.getNodes(); diff --git a/src/test/java/SimpleGraphTest.java b/src/test/java/SimpleGraphTest.java new file mode 100644 index 0000000..4759b57 --- /dev/null +++ b/src/test/java/SimpleGraphTest.java @@ -0,0 +1,110 @@ +import com.carrotsearch.hppc.IntDoubleHashMap; +import com.carrotsearch.hppc.IntDoubleMap; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.util.HintsMap; +import com.graphhopper.routing.util.RacingBikeFlagEncoder; +import com.graphhopper.routing.weighting.Weighting; +import com.graphhopper.storage.GraphBuilder; +import com.graphhopper.storage.GraphHopperStorage; +import com.graphhopper.util.EdgeIteratorState; +import gurobi.GRB; +import gurobi.GRBEnv; +import gurobi.GRBException; +import gurobi.GRBModel; +import io.github.plastix.Constraints; +import io.github.plastix.GraphUtils; +import org.junit.Before; +import org.junit.Test; + +public class SimpleGraphTest { + + GraphHopperStorage graph; + FlagEncoder flagEncoder; + GRBModel model; + IntDoubleMap weights; + + @Before + public void setUp() throws Exception { + flagEncoder = new RacingBikeFlagEncoder(); + EncodingManager encodingManager = new EncodingManager(flagEncoder); + GraphBuilder graphBuilder = new GraphBuilder(encodingManager) + .setStore(false); + graph = graphBuilder.create(); + weights = new IntDoubleHashMap(); + + createGraph(); + + GRBEnv env = new GRBEnv("osm-test.log"); + model = new GRBModel(env); + + } + + private void createGraph() { + addEdge(0, 1, true, 1, 1); + addEdge(1, 2, true, 1, 1); + addEdge(2, 0, true, 1, 1); + + addEdge(3, 4, true, 1, 1); + addEdge(4, 5, true, 1, 1); + addEdge(5, 3, true, 1, 1); + + } + + private void addEdge(int a, int b, boolean bidirectional, double cost, double score) { + EdgeIteratorState edge = graph.edge(a, b, cost, bidirectional); + weights.put(edge.getEdge(), score); + } + + @Test + public void optimizeRoute_disconnectedGraph() { + try { + GraphUtils graphUtils = new GraphUtils(graph, flagEncoder, new TestWeighting(weights)); + Constraints constraints = new Constraints(graph, model, graphUtils, 0, 4); + constraints.setupConstraints(); + model.set(GRB.IntParam.LogToConsole, 0); + model.optimize(); + } catch(GRBException e) { + e.printStackTrace(); + } + } + + private static class TestWeighting implements Weighting { + + private IntDoubleMap weights; + + TestWeighting(IntDoubleMap weights) { + this.weights = weights; + } + + @Override + public double getMinWeight(double distance) { + return 0; + } + + @Override + public double calcWeight(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) { + return weights.get(edgeState.getEdge()); + } + + @Override + public long calcMillis(EdgeIteratorState edgeState, boolean reverse, int prevOrNextEdgeId) { + return 0; + } + + @Override + public FlagEncoder getFlagEncoder() { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public boolean matches(HintsMap map) { + return false; + } + } +} From 8bdbe6f9f2edf00fff478c4d5478b119d95b472f Mon Sep 17 00:00:00 2001 From: Plastix Date: Wed, 31 Jan 2018 10:17:01 -0500 Subject: [PATCH 04/18] More work on unit tests --- .../java/io/github/plastix/Constraints.java | 28 ++--- src/main/java/io/github/plastix/Main.java | 6 +- src/main/java/io/github/plastix/Vars.java | 21 ++-- src/test/java/SimpleGraphTest.java | 116 +++++++++++++++--- 4 files changed, 124 insertions(+), 47 deletions(-) diff --git a/src/main/java/io/github/plastix/Constraints.java b/src/main/java/io/github/plastix/Constraints.java index 2c162d3..93f0c37 100644 --- a/src/main/java/io/github/plastix/Constraints.java +++ b/src/main/java/io/github/plastix/Constraints.java @@ -8,23 +8,19 @@ public class Constraints { - private final double MAX_COST; private Graph graph; private GRBModel model; + private Vars vars; private GraphUtils graphUtils; - private int START_NODE_ID; - public Constraints(Graph graph, GRBModel model, GraphUtils graphUtils, int START_NODE_ID, double maxCost) { + public Constraints(Graph graph, GRBModel model, Vars vars, GraphUtils graphUtils) { this.graph = graph; this.model = model; + this.vars = vars; this.graphUtils = graphUtils; - this.START_NODE_ID = START_NODE_ID; - this.MAX_COST = maxCost; } - public void setupConstraints() throws GRBException { - Vars vars = new Vars(graph, model, graphUtils); - vars.addVarsToModel(); + public void setupConstraints(int startNodeId, double maxCostMeters) throws GRBException { // (1a) // Objective maximizes total collected score of all roads @@ -32,7 +28,7 @@ public void setupConstraints() throws GRBException { // (1b) // Limit length of path - GRBLinExpr maxCost = new GRBLinExpr(); + GRBLinExpr maxCostConstraint = new GRBLinExpr(); AllEdgesIterator edges = graph.getAllEdges(); while(edges.next()) { @@ -44,8 +40,8 @@ public void setupConstraints() throws GRBException { objective.addTerm(edgeScore, forward); objective.addTerm(edgeScore, backward); - maxCost.addTerm(edgeDist, forward); - maxCost.addTerm(edgeDist, backward); + maxCostConstraint.addTerm(edgeDist, forward); + maxCostConstraint.addTerm(edgeDist, backward); // (1j) GRBLinExpr arcConstraint = new GRBLinExpr(); @@ -55,7 +51,7 @@ public void setupConstraints() throws GRBException { } model.setObjective(objective, GRB.MAXIMIZE); - model.addConstr(maxCost, GRB.LESS_EQUAL, MAX_COST, "max_cost"); + model.addConstr(maxCostConstraint, GRB.LESS_EQUAL, maxCostMeters, "max_cost"); int numNodes = graph.getNodes(); for(int i = 0; i < numNodes; i++) { @@ -93,12 +89,12 @@ public void setupConstraints() throws GRBException { // (1h)/(1i) // Start vertex must be visited exactly once - GRBVar startNode = vars.getVertexVar(START_NODE_ID); - startNode.set(GRB.DoubleAttr.LB, 1); - startNode.set(GRB.DoubleAttr.UB, 1); + GRBVar startNodeVar = vars.getVertexVar(startNodeId); + startNodeVar.set(GRB.DoubleAttr.LB, 1); + startNodeVar.set(GRB.DoubleAttr.UB, 1); // Must set LazyConstraints parameter when using lazy constraints model.set(GRB.IntParam.LazyConstraints, 1); - model.setCallback(new SubtourConstraint(vars, START_NODE_ID, graphUtils)); + model.setCallback(new SubtourConstraint(vars, startNodeId, graphUtils)); } } diff --git a/src/main/java/io/github/plastix/Main.java b/src/main/java/io/github/plastix/Main.java index 5975d19..1c4d25a 100644 --- a/src/main/java/io/github/plastix/Main.java +++ b/src/main/java/io/github/plastix/Main.java @@ -31,8 +31,10 @@ public class Main { private static void setupSolver() throws GRBException { env = new GRBEnv("osm.log"); model = new GRBModel(env); - Constraints constraints = new Constraints(graph, model, graphUtils, START_NODE_ID, params.getMaxCost()); - constraints.setupConstraints(); + Vars vars = new Vars(graph, model, graphUtils); + vars.addVarsToModel(); + Constraints constraints = new Constraints(graph, model, vars, graphUtils); + constraints.setupConstraints(START_NODE_ID, params.getMaxCost()); } private static void runSolver() throws GRBException { diff --git a/src/main/java/io/github/plastix/Vars.java b/src/main/java/io/github/plastix/Vars.java index 8d169f4..b66371b 100644 --- a/src/main/java/io/github/plastix/Vars.java +++ b/src/main/java/io/github/plastix/Vars.java @@ -4,7 +4,6 @@ import com.carrotsearch.hppc.IntIntMap; import com.carrotsearch.hppc.IntObjectHashMap; import com.carrotsearch.hppc.IntObjectMap; -import com.carrotsearch.hppc.cursors.IntObjectCursor; import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.storage.Graph; import com.graphhopper.util.EdgeIterator; @@ -13,6 +12,7 @@ import gurobi.GRBModel; import gurobi.GRBVar; +import java.util.ArrayList; import java.util.Arrays; public class Vars { @@ -26,7 +26,7 @@ public class Vars { private IntObjectMap backwardArcs; private IntIntMap arcBaseIds; // Records original "direction" of arc when processed. - Vars(Graph graph, GRBModel model, GraphUtils graphUtils) { + public Vars(Graph graph, GRBModel model, GraphUtils graphUtils) { this.graph = graph; this.model = model; this.graphUtils = graphUtils; @@ -67,6 +67,9 @@ public void addVarsToModel() throws GRBException { if(!graphUtils.isBackward(edges)) { backward.set(GRB.DoubleAttr.UB, 0); } + + System.out.println(edgeId + ": " + baseNode + "->" + edges.getAdjNode() + " " + + graphUtils.isForward(edges) + " " + graphUtils.isBackward(edges)); } } @@ -95,17 +98,13 @@ public GRBVar[] getVertexVars() { } public GRBVar[] getArcVars() { - GRBVar[] result = new GRBVar[forwardArcs.size() * 2]; - - int i = 0; - for(IntObjectCursor forwardArc : forwardArcs) { - result[i++] = forwardArc.value; - } + ArrayList result = new ArrayList<>(); - for(IntObjectCursor backwardArc : backwardArcs) { - result[i++] = backwardArc.value; + for(int i = 0; i < forwardArcs.size(); i++) { + result.add(forwardArcs.get(i)); + result.add(backwardArcs.get(i)); } - return result; + return result.toArray(new GRBVar[0]); } } diff --git a/src/test/java/SimpleGraphTest.java b/src/test/java/SimpleGraphTest.java index 4759b57..bf27068 100644 --- a/src/test/java/SimpleGraphTest.java +++ b/src/test/java/SimpleGraphTest.java @@ -14,14 +14,30 @@ import gurobi.GRBModel; import io.github.plastix.Constraints; import io.github.plastix.GraphUtils; +import io.github.plastix.Vars; +import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; +import java.util.Arrays; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@SuppressWarnings("WeakerAccess") public class SimpleGraphTest { + static final double FP_PRECISION = 0.01; + + GRBEnv env; + GRBModel model; + Vars vars; + Constraints constraints; + GraphHopperStorage graph; + GraphUtils graphUtils; FlagEncoder flagEncoder; - GRBModel model; IntDoubleMap weights; @Before @@ -33,14 +49,63 @@ public void setUp() throws Exception { graph = graphBuilder.create(); weights = new IntDoubleHashMap(); - createGraph(); - - GRBEnv env = new GRBEnv("osm-test.log"); + env = new GRBEnv(); model = new GRBModel(env); + graphUtils = new GraphUtils(graph, flagEncoder, new TestWeighting(weights)); + vars = new Vars(graph, model, graphUtils); + constraints = new Constraints(graph, model, vars, graphUtils); + + model.set(GRB.IntParam.LogToConsole, 0); + } + @After + public void tearDown() throws Exception { + env.dispose(); + model.dispose(); + graph.close(); } - private void createGraph() { + private void addEdge(int a, int b, boolean bidirectional, double cost, double score) { + EdgeIteratorState edge = graph.edge(a, b, cost, bidirectional); + weights.put(edge.getEdge(), score); + } + + @Test + public void singleThreeCycle() throws GRBException { + addEdge(0, 1, false, 1, 1); + addEdge(1, 2, false, 1, 1); + addEdge(2, 0, false, 1, 1); + + vars.addVarsToModel(); + constraints.setupConstraints(0, 3); + model.optimize(); + + assertHasSolution(); + printSolution(); + + double score = model.get(GRB.DoubleAttr.ObjVal); + assertEquals(3, score, FP_PRECISION); + } + + @Ignore + @Test + public void singleThreeCycle_limitedBudget() throws GRBException { + addEdge(0, 1, true, 1, 1); + addEdge(1, 2, true, 1, 1); + addEdge(2, 0, true, 1, 1); + + vars.addVarsToModel(); + constraints.setupConstraints(0, 2); + model.optimize(); + + double score = model.get(GRB.DoubleAttr.ObjVal); + assertEquals(2, score, FP_PRECISION); + } + + + @Ignore + @Test + public void twoDisconnectedThreeCycles() throws GRBException { addEdge(0, 1, true, 1, 1); addEdge(1, 2, true, 1, 1); addEdge(2, 0, true, 1, 1); @@ -49,23 +114,38 @@ private void createGraph() { addEdge(4, 5, true, 1, 1); addEdge(5, 3, true, 1, 1); + vars.addVarsToModel(); + constraints.setupConstraints(0, 4); + model.optimize(); + + double score = model.get(GRB.DoubleAttr.ObjVal); + + assertEquals(3, score, FP_PRECISION); } - private void addEdge(int a, int b, boolean bidirectional, double cost, double score) { - EdgeIteratorState edge = graph.edge(a, b, cost, bidirectional); - weights.put(edge.getEdge(), score); + private void printSolution() throws GRBException { + System.out.println("---- Final Solution ----"); + double[] arcs = model.get(GRB.DoubleAttr.X, vars.getArcVars()); + + StringBuilder arcString = new StringBuilder(); + + for(int i = 0; i < arcs.length - 1; i += 2) { + arcString.append("("); + arcString.append(arcs[i]); + arcString.append(", "); + arcString.append(arcs[i + 1]); + arcString.append(") "); + } + System.out.println("Arcs: " + arcString.toString()); + + double[] verts = model.get(GRB.DoubleAttr.X, vars.getVertexVars()); + System.out.println("Verts: " + Arrays.toString(verts)); + } - @Test - public void optimizeRoute_disconnectedGraph() { - try { - GraphUtils graphUtils = new GraphUtils(graph, flagEncoder, new TestWeighting(weights)); - Constraints constraints = new Constraints(graph, model, graphUtils, 0, 4); - constraints.setupConstraints(); - model.set(GRB.IntParam.LogToConsole, 0); - model.optimize(); - } catch(GRBException e) { - e.printStackTrace(); + private void assertHasSolution() throws GRBException { + if(model.get(GRB.IntAttr.Status) != GRB.Status.OPTIMAL) { + fail("Gurobi could not find an optimal solution!"); } } From aad2ab99b491b15f693092f648243c2fd525040d Mon Sep 17 00:00:00 2001 From: Plastix Date: Wed, 31 Jan 2018 10:54:55 -0500 Subject: [PATCH 05/18] Make singleDirectedThreeCycle test pass --- .../java/io/github/plastix/Constraints.java | 16 ++++++++---- .../io/github/plastix/SubtourConstraint.java | 8 +++--- src/main/java/io/github/plastix/Vars.java | 21 +++++++--------- src/test/java/SimpleGraphTest.java | 25 ++++++++++++++++--- 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/github/plastix/Constraints.java b/src/main/java/io/github/plastix/Constraints.java index 93f0c37..71a65b3 100644 --- a/src/main/java/io/github/plastix/Constraints.java +++ b/src/main/java/io/github/plastix/Constraints.java @@ -35,8 +35,8 @@ public void setupConstraints(int startNodeId, double maxCostMeters) throws GRBEx double edgeScore = graphUtils.getArcScore(edges); double edgeDist = edges.getDistance(); - GRBVar forward = vars.getArcVar(edges); - GRBVar backward = vars.getComplementArcVar(edges); + GRBVar forward = vars.getArcVar(edges, false); + GRBVar backward = vars.getArcVar(edges, true); objective.addTerm(edgeScore, forward); objective.addTerm(edgeScore, backward); @@ -61,18 +61,24 @@ public void setupConstraints(int startNodeId, double maxCostMeters) throws GRBEx EdgeIterator incoming = graphUtils.incomingEdges(i); while(incoming.next()) { incomingIds.add(incoming.getEdge()); - edgeCounts.addTerm(1, vars.getArcVar(incoming)); + edgeCounts.addTerm(1, vars.getArcVar(incoming, true)); + + System.out.println("Incoming: " + i); + System.out.println("(Edge: " + incoming.getEdge() + ") " + incoming.getBaseNode() + " <- " + incoming.getAdjNode()); } EdgeIterator outgoing = graphUtils.outgoingEdges(i); while(outgoing.next()) { - GRBVar arc = vars.getArcVar(outgoing); + GRBVar arc = vars.getArcVar(outgoing, false); // Check if we already recorded it as an incoming edge if(incomingIds.contains(outgoing.getEdge())) { edgeCounts.remove(arc); } else { edgeCounts.addTerm(-1, arc); } + + System.out.println("Outgoing: " + i); + System.out.println("(Edge: " + outgoing.getEdge() + ") " + outgoing.getBaseNode() + " -> " + outgoing.getAdjNode()); } model.addConstr(edgeCounts, GRB.EQUAL, 0, "edge_counts"); @@ -81,7 +87,7 @@ public void setupConstraints(int startNodeId, double maxCostMeters) throws GRBEx GRBLinExpr vertexVisits = new GRBLinExpr(); outgoing = graphUtils.outgoingEdges(i); while(outgoing.next()) { - vertexVisits.addTerm(1, vars.getArcVar(outgoing)); + vertexVisits.addTerm(1, vars.getArcVar(outgoing, false)); } vertexVisits.addTerm(-1, vars.getVertexVar(i)); model.addConstr(vertexVisits, GRB.EQUAL, 0, "vertex_visits"); diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index 81710d3..70b5e1d 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -28,7 +28,6 @@ protected void callback() { if(where == GRB.CB_MIPSOL) { // Found an integer feasible solution IntHashSet visitedVertices = getReachableVertexSubset(START_NODE_ID); - visitedVertices.remove(START_NODE_ID); int numVerticesInSolution = numVerticesInSolution(); System.out.println("-- Callback --"); @@ -38,7 +37,8 @@ protected void callback() { // If the number of vertices we can reach from the start is not the number of vertices we // visit in the entire solution, we have a disconnected tour - if(visitedVertices.size() != numVerticesInSolution) { + if(visitedVertices.size() < numVerticesInSolution) { + visitedVertices.remove(START_NODE_ID); // Add sub-tour elimination constraint GRBLinExpr subtourConstraint = new GRBLinExpr(); @@ -50,7 +50,7 @@ protected void callback() { EdgeIterator outgoing = graphUtils.outgoingEdges(vertexId); while(outgoing.next()) { - subtourConstraint.addTerm(1, vars.getArcVar(outgoing)); + subtourConstraint.addTerm(1, vars.getArcVar(outgoing, false)); totalOutgoingEdges += 1; } @@ -95,7 +95,7 @@ private IntHashSet getReachableVertexSubset(int startNode) throws GRBException { EdgeIterator iter = explorer.setBaseNode(current); while(iter.next()) { int connectedId = iter.getAdjNode(); - if(getSolution(vars.getArcVar(iter)) > 0.5) { + if(getSolution(vars.getArcVar(iter, false)) > 0.5) { stack.addLast(connectedId); } } diff --git a/src/main/java/io/github/plastix/Vars.java b/src/main/java/io/github/plastix/Vars.java index b66371b..0c1524e 100644 --- a/src/main/java/io/github/plastix/Vars.java +++ b/src/main/java/io/github/plastix/Vars.java @@ -68,25 +68,22 @@ public void addVarsToModel() throws GRBException { backward.set(GRB.DoubleAttr.UB, 0); } - System.out.println(edgeId + ": " + baseNode + "->" + edges.getAdjNode() + " " + + System.out.println(edgeId + ": " + baseNode + " -> " + edges.getAdjNode() + " " + graphUtils.isForward(edges) + " " + graphUtils.isBackward(edges)); } } - private IntObjectMap getMap(EdgeIterator edge) { - return arcBaseIds.get(edge.getEdge()) == edge.getBaseNode() ? forwardArcs : backwardArcs; - } - - private IntObjectMap getComplementMap(EdgeIterator edge) { - return arcBaseIds.get(edge.getEdge()) != edge.getBaseNode() ? forwardArcs : backwardArcs; + private IntObjectMap getMap(EdgeIterator edge, boolean reverse) { + int baseNode = edge.getBaseNode(); + if(reverse) { + baseNode = edge.getAdjNode(); + } + return arcBaseIds.get(edge.getEdge()) == baseNode ? forwardArcs : backwardArcs; } - public GRBVar getArcVar(EdgeIterator edge) { - return getMap(edge).get(edge.getEdge()); - } - public GRBVar getComplementArcVar(EdgeIterator edge) { - return getComplementMap(edge).get(edge.getEdge()); + public GRBVar getArcVar(EdgeIterator edge, boolean reverse) { + return getMap(edge, reverse).get(edge.getEdge()); } public GRBVar getVertexVar(int id) { diff --git a/src/test/java/SimpleGraphTest.java b/src/test/java/SimpleGraphTest.java index bf27068..ab30294 100644 --- a/src/test/java/SimpleGraphTest.java +++ b/src/test/java/SimpleGraphTest.java @@ -71,7 +71,7 @@ private void addEdge(int a, int b, boolean bidirectional, double cost, double sc } @Test - public void singleThreeCycle() throws GRBException { + public void singleDirectedThreeCycle() throws GRBException { addEdge(0, 1, false, 1, 1); addEdge(1, 2, false, 1, 1); addEdge(2, 0, false, 1, 1); @@ -87,17 +87,36 @@ public void singleThreeCycle() throws GRBException { assertEquals(3, score, FP_PRECISION); } - @Ignore @Test - public void singleThreeCycle_limitedBudget() throws GRBException { + public void singleUndirectedThreeCycle() throws GRBException { addEdge(0, 1, true, 1, 1); addEdge(1, 2, true, 1, 1); addEdge(2, 0, true, 1, 1); + vars.addVarsToModel(); + constraints.setupConstraints(0, 3); + model.optimize(); + + assertHasSolution(); + printSolution(); + + double score = model.get(GRB.DoubleAttr.ObjVal); + assertEquals(3, score, FP_PRECISION); + } + + @Ignore + @Test + public void singleThreeCycle_limitedBudget() throws GRBException { + addEdge(0, 1, false, 1, 1); + addEdge(1, 2, false, 1, 1); + addEdge(2, 0, false, 1, 1); + vars.addVarsToModel(); constraints.setupConstraints(0, 2); model.optimize(); + assertHasSolution(); + double score = model.get(GRB.DoubleAttr.ObjVal); assertEquals(2, score, FP_PRECISION); } From 1dfa58dbac2347e230cb7f758f20811fe58bfe29 Mon Sep 17 00:00:00 2001 From: Plastix Date: Wed, 31 Jan 2018 11:37:59 -0500 Subject: [PATCH 06/18] Fix and refactor tests --- .../java/io/github/plastix/Constraints.java | 20 ++---- src/main/java/io/github/plastix/Vars.java | 4 +- ...leGraphTest.java => SimpleGraphTests.java} | 71 +++++++++---------- 3 files changed, 40 insertions(+), 55 deletions(-) rename src/test/java/{SimpleGraphTest.java => SimpleGraphTests.java} (81%) diff --git a/src/main/java/io/github/plastix/Constraints.java b/src/main/java/io/github/plastix/Constraints.java index 71a65b3..5cd958f 100644 --- a/src/main/java/io/github/plastix/Constraints.java +++ b/src/main/java/io/github/plastix/Constraints.java @@ -57,28 +57,20 @@ public void setupConstraints(int startNodeId, double maxCostMeters) throws GRBEx for(int i = 0; i < numNodes; i++) { // (1d) GRBLinExpr edgeCounts = new GRBLinExpr(); - IntHashSet incomingIds = new IntHashSet(); EdgeIterator incoming = graphUtils.incomingEdges(i); +// System.out.println("Incoming: " + i); while(incoming.next()) { - incomingIds.add(incoming.getEdge()); edgeCounts.addTerm(1, vars.getArcVar(incoming, true)); - - System.out.println("Incoming: " + i); - System.out.println("(Edge: " + incoming.getEdge() + ") " + incoming.getBaseNode() + " <- " + incoming.getAdjNode()); +// System.out.println("(Edge: " + incoming.getEdge() + ") " + incoming.getBaseNode() + " <- " + incoming.getAdjNode()); } EdgeIterator outgoing = graphUtils.outgoingEdges(i); +// System.out.println("Outgoing: " + i); while(outgoing.next()) { GRBVar arc = vars.getArcVar(outgoing, false); - // Check if we already recorded it as an incoming edge - if(incomingIds.contains(outgoing.getEdge())) { - edgeCounts.remove(arc); - } else { - edgeCounts.addTerm(-1, arc); - } - - System.out.println("Outgoing: " + i); - System.out.println("(Edge: " + outgoing.getEdge() + ") " + outgoing.getBaseNode() + " -> " + outgoing.getAdjNode()); + edgeCounts.addTerm(-1, arc); + +// System.out.println("(Edge: " + outgoing.getEdge() + ") " + outgoing.getBaseNode() + " -> " + outgoing.getAdjNode()); } model.addConstr(edgeCounts, GRB.EQUAL, 0, "edge_counts"); diff --git a/src/main/java/io/github/plastix/Vars.java b/src/main/java/io/github/plastix/Vars.java index 0c1524e..2fb1ced 100644 --- a/src/main/java/io/github/plastix/Vars.java +++ b/src/main/java/io/github/plastix/Vars.java @@ -68,8 +68,8 @@ public void addVarsToModel() throws GRBException { backward.set(GRB.DoubleAttr.UB, 0); } - System.out.println(edgeId + ": " + baseNode + " -> " + edges.getAdjNode() + " " + - graphUtils.isForward(edges) + " " + graphUtils.isBackward(edges)); +// System.out.println(edgeId + ": " + baseNode + " -> " + edges.getAdjNode() + " " + +// graphUtils.isForward(edges) + " " + graphUtils.isBackward(edges)); } } diff --git a/src/test/java/SimpleGraphTest.java b/src/test/java/SimpleGraphTests.java similarity index 81% rename from src/test/java/SimpleGraphTest.java rename to src/test/java/SimpleGraphTests.java index ab30294..ee1730c 100644 --- a/src/test/java/SimpleGraphTest.java +++ b/src/test/java/SimpleGraphTests.java @@ -17,7 +17,6 @@ import io.github.plastix.Vars; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import java.util.Arrays; @@ -26,7 +25,7 @@ import static org.junit.Assert.fail; @SuppressWarnings("WeakerAccess") -public class SimpleGraphTest { +public class SimpleGraphTests { static final double FP_PRECISION = 0.01; @@ -70,21 +69,21 @@ private void addEdge(int a, int b, boolean bidirectional, double cost, double sc weights.put(edge.getEdge(), score); } + private void runSolver(int startNode, int maxCost) throws GRBException { + vars.addVarsToModel(); + constraints.setupConstraints(startNode, maxCost); + model.optimize(); + } + @Test public void singleDirectedThreeCycle() throws GRBException { addEdge(0, 1, false, 1, 1); addEdge(1, 2, false, 1, 1); addEdge(2, 0, false, 1, 1); - vars.addVarsToModel(); - constraints.setupConstraints(0, 3); - model.optimize(); - + runSolver(0, 3); assertHasSolution(); - printSolution(); - - double score = model.get(GRB.DoubleAttr.ObjVal); - assertEquals(3, score, FP_PRECISION); + assertSolution(3); } @Test @@ -93,36 +92,23 @@ public void singleUndirectedThreeCycle() throws GRBException { addEdge(1, 2, true, 1, 1); addEdge(2, 0, true, 1, 1); - vars.addVarsToModel(); - constraints.setupConstraints(0, 3); - model.optimize(); - + runSolver(0, 3); assertHasSolution(); - printSolution(); - - double score = model.get(GRB.DoubleAttr.ObjVal); - assertEquals(3, score, FP_PRECISION); + assertSolution(3); } - @Ignore @Test - public void singleThreeCycle_limitedBudget() throws GRBException { - addEdge(0, 1, false, 1, 1); - addEdge(1, 2, false, 1, 1); - addEdge(2, 0, false, 1, 1); - - vars.addVarsToModel(); - constraints.setupConstraints(0, 2); - model.optimize(); - - assertHasSolution(); + public void singleUndirectedThreeCycle_limitedBudget() throws GRBException { + addEdge(0, 1, true, 1, 1); + addEdge(1, 2, true, 1, 1); + addEdge(2, 0, true, 1, 1); - double score = model.get(GRB.DoubleAttr.ObjVal); - assertEquals(2, score, FP_PRECISION); + runSolver(0, 2); + // We can't find a solution here since we aren't allowed to take a road backwards + assertNoSolution(); } - @Ignore @Test public void twoDisconnectedThreeCycles() throws GRBException { addEdge(0, 1, true, 1, 1); @@ -133,13 +119,9 @@ public void twoDisconnectedThreeCycles() throws GRBException { addEdge(4, 5, true, 1, 1); addEdge(5, 3, true, 1, 1); - vars.addVarsToModel(); - constraints.setupConstraints(0, 4); - model.optimize(); - - double score = model.get(GRB.DoubleAttr.ObjVal); - - assertEquals(3, score, FP_PRECISION); + runSolver(0, 3); + assertHasSolution(); + assertSolution(3); } private void printSolution() throws GRBException { @@ -168,6 +150,17 @@ private void assertHasSolution() throws GRBException { } } + private void assertNoSolution() throws GRBException { + if(model.get(GRB.IntAttr.Status) != GRB.Status.INFEASIBLE) { + fail("Gurobi found an optimal solution!"); + } + } + + private void assertSolution(double score) throws GRBException { + double actualScore = model.get(GRB.DoubleAttr.ObjVal); + assertEquals(actualScore, score, FP_PRECISION); + } + private static class TestWeighting implements Weighting { private IntDoubleMap weights; From 706a2fa03da3805923f190447764bb392f04144c Mon Sep 17 00:00:00 2001 From: Plastix Date: Wed, 31 Jan 2018 11:42:58 -0500 Subject: [PATCH 07/18] Add test for complete graph of order 4 --- src/test/java/SimpleGraphTests.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/SimpleGraphTests.java b/src/test/java/SimpleGraphTests.java index ee1730c..f239a9b 100644 --- a/src/test/java/SimpleGraphTests.java +++ b/src/test/java/SimpleGraphTests.java @@ -124,6 +124,20 @@ public void twoDisconnectedThreeCycles() throws GRBException { assertSolution(3); } + @Test + public void directedKFour() throws GRBException { + addEdge(0, 1, false, 1, 2); + addEdge(1, 2, false, 1, 2); + addEdge(2, 3, false, 1, 2); + addEdge(3, 0, false, 1, 2); + addEdge(0, 2, false, 1, 1); + addEdge(1, 3, false, 1, 1); + + runSolver(0, 4); + assertHasSolution(); + assertSolution(8); + } + private void printSolution() throws GRBException { System.out.println("---- Final Solution ----"); double[] arcs = model.get(GRB.DoubleAttr.X, vars.getArcVars()); From ad5964befc00bd92f54a87ed2183cfb66247287a Mon Sep 17 00:00:00 2001 From: Plastix Date: Wed, 31 Jan 2018 16:25:22 -0500 Subject: [PATCH 08/18] Fix subtour constraint --- .../io/github/plastix/SubtourConstraint.java | 30 ++++++++++++++----- src/test/java/SimpleGraphTests.java | 17 ++++++++++- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index 70b5e1d..86be938 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -5,10 +5,7 @@ import com.carrotsearch.hppc.cursors.IntCursor; import com.graphhopper.util.EdgeExplorer; import com.graphhopper.util.EdgeIterator; -import gurobi.GRB; -import gurobi.GRBCallback; -import gurobi.GRBException; -import gurobi.GRBLinExpr; +import gurobi.*; public class SubtourConstraint extends GRBCallback { @@ -45,21 +42,26 @@ protected void callback() { int sumVertexVisits = 0; int totalOutgoingEdges = 0; + double lhs = 0; for(IntCursor cursor : visitedVertices) { int vertexId = cursor.value; EdgeIterator outgoing = graphUtils.outgoingEdges(vertexId); while(outgoing.next()) { - subtourConstraint.addTerm(1, vars.getArcVar(outgoing, false)); + GRBVar var = vars.getArcVar(outgoing, false); + subtourConstraint.addTerm(1, var); totalOutgoingEdges += 1; + + lhs += getSolution(var); } + sumVertexVisits += getSolution(vars.getVertexVar(vertexId)); } double rhs = ((double) sumVertexVisits) / ((double) totalOutgoingEdges); - System.out.println("adding lazy constraint!"); - addLazy(subtourConstraint, GRB.GREATER_EQUAL, rhs); + System.out.println("adding lazy constraint! " + lhs + " <= " + rhs); + addLazy(subtourConstraint, GRB.LESS_EQUAL, rhs); } } @@ -82,6 +84,20 @@ private int numVerticesInSolution() throws GRBException { return visited; } + private IntHashSet verticesInSolution() throws GRBException { + double[] values = getSolution(vars.getVertexVars()); + + IntHashSet result = new IntHashSet(); + + for(int i = 0; i < values.length; i++) { + if(values[i] > 0) { + result.add(i); + } + } + + return result; + } + private IntHashSet getReachableVertexSubset(int startNode) throws GRBException { EdgeExplorer explorer = graphUtils.getEdgeExplorer(); IntArrayDeque stack = new IntArrayDeque(); diff --git a/src/test/java/SimpleGraphTests.java b/src/test/java/SimpleGraphTests.java index f239a9b..085540c 100644 --- a/src/test/java/SimpleGraphTests.java +++ b/src/test/java/SimpleGraphTests.java @@ -124,6 +124,21 @@ public void twoDisconnectedThreeCycles() throws GRBException { assertSolution(3); } + @Test + public void twoDisconnectedThreeCycles_largeBudget() throws GRBException { + addEdge(0, 1, false, 1, 1); + addEdge(1, 2, false, 1, 1); + addEdge(2, 0, false, 1, 1); + + addEdge(3, 4, false, 1, 1); + addEdge(4, 5, false, 1, 1); + addEdge(5, 3, false, 1, 1); + + runSolver(0, 6); + assertHasSolution(); + assertSolution(3); + } + @Test public void directedKFour() throws GRBException { addEdge(0, 1, false, 1, 2); @@ -172,7 +187,7 @@ private void assertNoSolution() throws GRBException { private void assertSolution(double score) throws GRBException { double actualScore = model.get(GRB.DoubleAttr.ObjVal); - assertEquals(actualScore, score, FP_PRECISION); + assertEquals(score, actualScore, FP_PRECISION); } private static class TestWeighting implements Weighting { From a5e96db1648208eff41e8c27708f00f1cbc7e9d2 Mon Sep 17 00:00:00 2001 From: Plastix Date: Thu, 1 Feb 2018 06:57:37 -0500 Subject: [PATCH 09/18] Add cactus graph test --- src/test/java/SimpleGraphTests.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/test/java/SimpleGraphTests.java b/src/test/java/SimpleGraphTests.java index 085540c..2c6a901 100644 --- a/src/test/java/SimpleGraphTests.java +++ b/src/test/java/SimpleGraphTests.java @@ -153,6 +153,25 @@ public void directedKFour() throws GRBException { assertSolution(8); } + @Test + public void simpleDirectedCactusGraph() throws GRBException { + addEdge(0, 1, false, 1, 1); + addEdge(1, 2, false, 1, 1); + addEdge(2, 0, false, 1, 1); + + addEdge(1, 3, false, 1, 1); + addEdge(3, 4, false, 1, 3); + addEdge(4, 1, false, 1, 1); + + addEdge(2, 5, false, 1, 1); + addEdge(5, 6, false, 1, 1); + addEdge(6, 2, false, 1, 1); + + runSolver(0, 6); + assertHasSolution(); + assertSolution(8); + } + private void printSolution() throws GRBException { System.out.println("---- Final Solution ----"); double[] arcs = model.get(GRB.DoubleAttr.X, vars.getArcVars()); From b065b6854c2629294cf651f0e500ed4fb6b48fe1 Mon Sep 17 00:00:00 2001 From: Plastix Date: Thu, 1 Feb 2018 12:11:18 -0500 Subject: [PATCH 10/18] More tests and small fix to subtour constraint --- .../java/io/github/plastix/Constraints.java | 6 ---- src/main/java/io/github/plastix/Main.java | 3 +- .../io/github/plastix/SubtourConstraint.java | 30 +++++----------- src/main/java/io/github/plastix/Vars.java | 3 -- src/test/java/SimpleGraphTests.java | 36 +++++++++++++++++++ 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/main/java/io/github/plastix/Constraints.java b/src/main/java/io/github/plastix/Constraints.java index 5cd958f..377abfc 100644 --- a/src/main/java/io/github/plastix/Constraints.java +++ b/src/main/java/io/github/plastix/Constraints.java @@ -1,6 +1,5 @@ package io.github.plastix; -import com.carrotsearch.hppc.IntHashSet; import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.storage.Graph; import com.graphhopper.util.EdgeIterator; @@ -58,19 +57,14 @@ public void setupConstraints(int startNodeId, double maxCostMeters) throws GRBEx // (1d) GRBLinExpr edgeCounts = new GRBLinExpr(); EdgeIterator incoming = graphUtils.incomingEdges(i); -// System.out.println("Incoming: " + i); while(incoming.next()) { edgeCounts.addTerm(1, vars.getArcVar(incoming, true)); -// System.out.println("(Edge: " + incoming.getEdge() + ") " + incoming.getBaseNode() + " <- " + incoming.getAdjNode()); } EdgeIterator outgoing = graphUtils.outgoingEdges(i); -// System.out.println("Outgoing: " + i); while(outgoing.next()) { GRBVar arc = vars.getArcVar(outgoing, false); edgeCounts.addTerm(-1, arc); - -// System.out.println("(Edge: " + outgoing.getEdge() + ") " + outgoing.getBaseNode() + " -> " + outgoing.getAdjNode()); } model.addConstr(edgeCounts, GRB.EQUAL, 0, "edge_counts"); diff --git a/src/main/java/io/github/plastix/Main.java b/src/main/java/io/github/plastix/Main.java index 1c4d25a..d894f9d 100644 --- a/src/main/java/io/github/plastix/Main.java +++ b/src/main/java/io/github/plastix/Main.java @@ -9,7 +9,6 @@ import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; import com.graphhopper.storage.index.QueryResult; -import gurobi.GRB; import gurobi.GRBEnv; import gurobi.GRBException; import gurobi.GRBModel; @@ -42,7 +41,7 @@ private static void runSolver() throws GRBException { System.out.println("Start position: " + params.getStartLat() + ", " + params.getStartLon() + " (Node " + START_NODE_ID + ")"); - model.set(GRB.IntParam.LogToConsole, 0); +// model.set(GRB.IntParam.LogToConsole, 0); model.optimize(); env.dispose(); diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index 86be938..4a661b0 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -27,10 +27,11 @@ protected void callback() { IntHashSet visitedVertices = getReachableVertexSubset(START_NODE_ID); int numVerticesInSolution = numVerticesInSolution(); - System.out.println("-- Callback --"); - System.out.println("Solution vertices: " + numVerticesInSolution); - System.out.println("Reachable vertices: " + visitedVertices.size()); - System.out.println("Solution arcs: " + numArcsInSolution()); +// System.out.println("-- Callback --"); +// System.out.println("Solution vertices: " + numVerticesInSolution); +// System.out.println("Reachable vertices: " + visitedVertices.size()); +// System.out.println("Solution arcs: " + numArcsInSolution()); + // If the number of vertices we can reach from the start is not the number of vertices we // visit in the entire solution, we have a disconnected tour @@ -49,13 +50,12 @@ protected void callback() { while(outgoing.next()) { GRBVar var = vars.getArcVar(outgoing, false); - subtourConstraint.addTerm(1, var); + if(getSolution(var) > 0) { + subtourConstraint.addTerm(1, var); + } totalOutgoingEdges += 1; - lhs += getSolution(var); } - - sumVertexVisits += getSolution(vars.getVertexVar(vertexId)); } @@ -84,20 +84,6 @@ private int numVerticesInSolution() throws GRBException { return visited; } - private IntHashSet verticesInSolution() throws GRBException { - double[] values = getSolution(vars.getVertexVars()); - - IntHashSet result = new IntHashSet(); - - for(int i = 0; i < values.length; i++) { - if(values[i] > 0) { - result.add(i); - } - } - - return result; - } - private IntHashSet getReachableVertexSubset(int startNode) throws GRBException { EdgeExplorer explorer = graphUtils.getEdgeExplorer(); IntArrayDeque stack = new IntArrayDeque(); diff --git a/src/main/java/io/github/plastix/Vars.java b/src/main/java/io/github/plastix/Vars.java index 2fb1ced..46168e3 100644 --- a/src/main/java/io/github/plastix/Vars.java +++ b/src/main/java/io/github/plastix/Vars.java @@ -67,9 +67,6 @@ public void addVarsToModel() throws GRBException { if(!graphUtils.isBackward(edges)) { backward.set(GRB.DoubleAttr.UB, 0); } - -// System.out.println(edgeId + ": " + baseNode + " -> " + edges.getAdjNode() + " " + -// graphUtils.isForward(edges) + " " + graphUtils.isBackward(edges)); } } diff --git a/src/test/java/SimpleGraphTests.java b/src/test/java/SimpleGraphTests.java index 2c6a901..b64a30a 100644 --- a/src/test/java/SimpleGraphTests.java +++ b/src/test/java/SimpleGraphTests.java @@ -139,6 +139,42 @@ public void twoDisconnectedThreeCycles_largeBudget() throws GRBException { assertSolution(3); } + @Test + public void twoConnectedThreeCycles() throws GRBException { + addEdge(0, 1, false, 1, 1); + addEdge(1, 2, false, 1, 1); + addEdge(2, 0, false, 1, 1); + + addEdge(2, 3, true, 2, 2); + + addEdge(3, 4, false, 1, 1); + addEdge(4, 5, false, 1, 1); + addEdge(5, 3, false, 1, 1); + + runSolver(0, 6); + assertHasSolution(); + assertSolution(3); + } + + @Test + public void sixCycleWithTwoThreeCycles() throws GRBException { + addEdge(0, 1, true, 1, 1); + addEdge(1, 2, true, 1, 1); + addEdge(2, 0, true, 1, 1); + + addEdge(2, 3, true, 1, 1); + addEdge(1, 4, true, 1, 1); + + addEdge(3, 4, true, 1, 1); + addEdge(4, 5, true, 1, 1); + addEdge(5, 3, true, 1, 1); + + runSolver(0, 6); + assertHasSolution(); + assertSolution(6); + } + + @Test public void directedKFour() throws GRBException { addEdge(0, 1, false, 1, 2); From 823d9d623e539a51c3988518f409dfd814233a43 Mon Sep 17 00:00:00 2001 From: Plastix Date: Thu, 1 Feb 2018 17:38:59 -0500 Subject: [PATCH 11/18] Misc cleanup --- src/main/java/io/github/plastix/Main.java | 4 +- .../io/github/plastix/SubtourConstraint.java | 20 +----- src/main/java/io/github/plastix/Vars.java | 11 --- src/test/java/SimpleGraphTests.java | 70 +++++++++++-------- 4 files changed, 42 insertions(+), 63 deletions(-) diff --git a/src/main/java/io/github/plastix/Main.java b/src/main/java/io/github/plastix/Main.java index d894f9d..6c91367 100644 --- a/src/main/java/io/github/plastix/Main.java +++ b/src/main/java/io/github/plastix/Main.java @@ -44,8 +44,8 @@ private static void runSolver() throws GRBException { // model.set(GRB.IntParam.LogToConsole, 0); model.optimize(); - env.dispose(); model.dispose(); + env.dispose(); hopper.close(); } @@ -63,7 +63,7 @@ private static void loadOSM() { FlagEncoder flagEncoder = encodingManager.getEncoder(params.getVehicle()); Weighting weighting = new BikePriorityWeighting(flagEncoder); - graph = hopper.getGraphHopperStorage().getBaseGraph(); + graph = hopper.getGraphHopperStorage(); graphUtils = new GraphUtils(graph, flagEncoder, weighting); START_NODE_ID = getStartNode(flagEncoder); diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index 4a661b0..3965923 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -27,12 +27,6 @@ protected void callback() { IntHashSet visitedVertices = getReachableVertexSubset(START_NODE_ID); int numVerticesInSolution = numVerticesInSolution(); -// System.out.println("-- Callback --"); -// System.out.println("Solution vertices: " + numVerticesInSolution); -// System.out.println("Reachable vertices: " + visitedVertices.size()); -// System.out.println("Solution arcs: " + numArcsInSolution()); - - // If the number of vertices we can reach from the start is not the number of vertices we // visit in the entire solution, we have a disconnected tour if(visitedVertices.size() < numVerticesInSolution) { @@ -97,7 +91,7 @@ private IntHashSet getReachableVertexSubset(int startNode) throws GRBException { EdgeIterator iter = explorer.setBaseNode(current); while(iter.next()) { int connectedId = iter.getAdjNode(); - if(getSolution(vars.getArcVar(iter, false)) > 0.5) { + if(getSolution(vars.getArcVar(iter, false)) > 0) { stack.addLast(connectedId); } } @@ -107,16 +101,4 @@ private IntHashSet getReachableVertexSubset(int startNode) throws GRBException { return explored; } - - private int numArcsInSolution() throws GRBException { - double[] values = getSolution(vars.getArcVars()); - - int visited = 0; - for(double value : values) { - if(value > 0) { - visited++; - } - } - return visited; - } } diff --git a/src/main/java/io/github/plastix/Vars.java b/src/main/java/io/github/plastix/Vars.java index 46168e3..f18acc9 100644 --- a/src/main/java/io/github/plastix/Vars.java +++ b/src/main/java/io/github/plastix/Vars.java @@ -90,15 +90,4 @@ public GRBVar getVertexVar(int id) { public GRBVar[] getVertexVars() { return verts; } - - public GRBVar[] getArcVars() { - ArrayList result = new ArrayList<>(); - - for(int i = 0; i < forwardArcs.size(); i++) { - result.add(forwardArcs.get(i)); - result.add(backwardArcs.get(i)); - } - - return result.toArray(new GRBVar[0]); - } } diff --git a/src/test/java/SimpleGraphTests.java b/src/test/java/SimpleGraphTests.java index b64a30a..d6b55d7 100644 --- a/src/test/java/SimpleGraphTests.java +++ b/src/test/java/SimpleGraphTests.java @@ -1,9 +1,6 @@ import com.carrotsearch.hppc.IntDoubleHashMap; import com.carrotsearch.hppc.IntDoubleMap; -import com.graphhopper.routing.util.EncodingManager; -import com.graphhopper.routing.util.FlagEncoder; -import com.graphhopper.routing.util.HintsMap; -import com.graphhopper.routing.util.RacingBikeFlagEncoder; +import com.graphhopper.routing.util.*; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.GraphBuilder; import com.graphhopper.storage.GraphHopperStorage; @@ -12,6 +9,7 @@ import gurobi.GRBEnv; import gurobi.GRBException; import gurobi.GRBModel; +import io.github.plastix.BikePriorityWeighting; import io.github.plastix.Constraints; import io.github.plastix.GraphUtils; import io.github.plastix.Vars; @@ -19,8 +17,6 @@ import org.junit.Before; import org.junit.Test; -import java.util.Arrays; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -75,6 +71,23 @@ private void runSolver(int startNode, int maxCost) throws GRBException { model.optimize(); } + @Test + public void singleArcGraph() throws GRBException { + addEdge(0, 1, false, 1, 1); + + runSolver(0, 2); + assertNoSolution(); + } + + @Test + public void disconnectedArcs() throws GRBException { + addEdge(0, 1, false, 1, 1); + addEdge(2, 3, false, 1, 1); + + runSolver(0, 2); + assertNoSolution(); + } + @Test public void singleDirectedThreeCycle() throws GRBException { addEdge(0, 1, false, 1, 1); @@ -125,16 +138,18 @@ public void twoDisconnectedThreeCycles() throws GRBException { } @Test - public void twoDisconnectedThreeCycles_largeBudget() throws GRBException { - addEdge(0, 1, false, 1, 1); - addEdge(1, 2, false, 1, 1); - addEdge(2, 0, false, 1, 1); - - addEdge(3, 4, false, 1, 1); - addEdge(4, 5, false, 1, 1); - addEdge(5, 3, false, 1, 1); + public void multipleDisconnectedThreeCycles() throws GRBException { + int numThreeCycles = 10; + int nodeId = 0; + + for(int i = 0; i < numThreeCycles; i++) { + addEdge(nodeId, nodeId + 1, false, 1, 1); + addEdge(nodeId + 1, nodeId + 2, false, 1, 1); + addEdge(nodeId + 2, nodeId, false, 1, 1); + nodeId += 3; + } - runSolver(0, 6); + runSolver(0, 3 * numThreeCycles); assertHasSolution(); assertSolution(3); } @@ -208,24 +223,17 @@ public void simpleDirectedCactusGraph() throws GRBException { assertSolution(8); } - private void printSolution() throws GRBException { - System.out.println("---- Final Solution ----"); - double[] arcs = model.get(GRB.DoubleAttr.X, vars.getArcVars()); - - StringBuilder arcString = new StringBuilder(); - - for(int i = 0; i < arcs.length - 1; i += 2) { - arcString.append("("); - arcString.append(arcs[i]); - arcString.append(", "); - arcString.append(arcs[i + 1]); - arcString.append(") "); + @Test + public void threeNodeMultiEdgeGraph() throws GRBException { + addEdge(0, 1, false, 1, 1); + addEdge(2, 0, false, 1, 1); + for(int i = 0; i < 20; i++) { + addEdge(1, 2, true, 1, 1); } - System.out.println("Arcs: " + arcString.toString()); - - double[] verts = model.get(GRB.DoubleAttr.X, vars.getVertexVars()); - System.out.println("Verts: " + Arrays.toString(verts)); + runSolver(0, 10); + assertHasSolution(); + assertSolution(9); } private void assertHasSolution() throws GRBException { From 11d3666163127b50d7a6e572ab79fa853b566c8f Mon Sep 17 00:00:00 2001 From: Plastix Date: Fri, 2 Feb 2018 07:39:39 -0500 Subject: [PATCH 12/18] More refactoring --- .../java/io/github/plastix/Constraints.java | 15 +++++- src/main/java/io/github/plastix/Main.java | 13 +----- src/main/java/io/github/plastix/Params.java | 14 ++++++ src/main/java/io/github/plastix/Vars.java | 4 +- src/main/resources/params.properties | 4 +- src/test/java/SimpleGraphTests.java | 46 +++++++++++++------ 6 files changed, 64 insertions(+), 32 deletions(-) diff --git a/src/main/java/io/github/plastix/Constraints.java b/src/main/java/io/github/plastix/Constraints.java index 377abfc..14ecda3 100644 --- a/src/main/java/io/github/plastix/Constraints.java +++ b/src/main/java/io/github/plastix/Constraints.java @@ -12,6 +12,9 @@ public class Constraints { private Vars vars; private GraphUtils graphUtils; + private GRBLinExpr maxCostConstraint; + private GRBLinExpr objective; + public Constraints(Graph graph, GRBModel model, Vars vars, GraphUtils graphUtils) { this.graph = graph; this.model = model; @@ -23,11 +26,11 @@ public void setupConstraints(int startNodeId, double maxCostMeters) throws GRBEx // (1a) // Objective maximizes total collected score of all roads - GRBLinExpr objective = new GRBLinExpr(); + objective = new GRBLinExpr(); // (1b) // Limit length of path - GRBLinExpr maxCostConstraint = new GRBLinExpr(); + maxCostConstraint = new GRBLinExpr(); AllEdgesIterator edges = graph.getAllEdges(); while(edges.next()) { @@ -89,4 +92,12 @@ public void setupConstraints(int startNodeId, double maxCostMeters) throws GRBEx model.set(GRB.IntParam.LazyConstraints, 1); model.setCallback(new SubtourConstraint(vars, startNodeId, graphUtils)); } + + public GRBLinExpr getMaxCostConstraint() { + return maxCostConstraint; + } + + public GRBLinExpr getObjective() { + return objective; + } } diff --git a/src/main/java/io/github/plastix/Main.java b/src/main/java/io/github/plastix/Main.java index 6c91367..5cac274 100644 --- a/src/main/java/io/github/plastix/Main.java +++ b/src/main/java/io/github/plastix/Main.java @@ -3,12 +3,10 @@ import com.graphhopper.GraphHopper; import com.graphhopper.reader.osm.GraphHopperOSM; import com.graphhopper.routing.util.AllEdgesIterator; -import com.graphhopper.routing.util.DefaultEdgeFilter; import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; -import com.graphhopper.storage.index.QueryResult; import gurobi.GRBEnv; import gurobi.GRBException; import gurobi.GRBModel; @@ -65,7 +63,7 @@ private static void loadOSM() { Weighting weighting = new BikePriorityWeighting(flagEncoder); graph = hopper.getGraphHopperStorage(); graphUtils = new GraphUtils(graph, flagEncoder, weighting); - START_NODE_ID = getStartNode(flagEncoder); + START_NODE_ID = params.getStartNode(hopper.getLocationIndex(), flagEncoder); AllEdgesIterator edges = graph.getAllEdges(); int nonTraversable = 0; @@ -80,15 +78,6 @@ private static void loadOSM() { graph.getAllEdges().getMaxId(), graph.getNodes(), nonTraversable, oneWay)); } - private static int getStartNode(FlagEncoder flagEncoder) { - QueryResult result = hopper.getLocationIndex().findClosest(params.getStartLat(), params.getStartLon(), - new DefaultEdgeFilter(flagEncoder)); - if(!result.isValid()) { - throw new RuntimeException("Unable to find node at start lat/lon!"); - } - return result.getClosestNode(); - } - public static void main(String[] args) { params = new Params(); params.loadParams(); diff --git a/src/main/java/io/github/plastix/Params.java b/src/main/java/io/github/plastix/Params.java index 30ec8e4..6a80c50 100644 --- a/src/main/java/io/github/plastix/Params.java +++ b/src/main/java/io/github/plastix/Params.java @@ -1,5 +1,10 @@ package io.github.plastix; +import com.graphhopper.routing.util.DefaultEdgeFilter; +import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.storage.index.LocationIndex; +import com.graphhopper.storage.index.QueryResult; + import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -60,4 +65,13 @@ public double getStartLon() { public String getVehicle() { return VEHICLE; } + + public int getStartNode(LocationIndex locationIndex, FlagEncoder flagEncoder) { + QueryResult result = locationIndex.findClosest(START_LAT, START_LON, + new DefaultEdgeFilter(flagEncoder)); + if(!result.isValid()) { + throw new RuntimeException("Unable to find node at start lat/lon!"); + } + return result.getClosestNode(); + } } diff --git a/src/main/java/io/github/plastix/Vars.java b/src/main/java/io/github/plastix/Vars.java index f18acc9..bc35d7c 100644 --- a/src/main/java/io/github/plastix/Vars.java +++ b/src/main/java/io/github/plastix/Vars.java @@ -12,7 +12,6 @@ import gurobi.GRBModel; import gurobi.GRBVar; -import java.util.ArrayList; import java.util.Arrays; public class Vars { @@ -84,6 +83,9 @@ public GRBVar getArcVar(EdgeIterator edge, boolean reverse) { } public GRBVar getVertexVar(int id) { + if(id < 0 || id >= graph.getNodes()) { + throw new IllegalArgumentException(String.format("Invalid node id %d", id)); + } return verts[id]; } diff --git a/src/main/resources/params.properties b/src/main/resources/params.properties index 9dcd291..b5daaa3 100644 --- a/src/main/resources/params.properties +++ b/src/main/resources/params.properties @@ -2,5 +2,5 @@ graphFile=src/main/resources/ny_capital_district.pbf graphFolder=src/main/resources/ny_capital_district-gh/ vehicle=racingbike maxCost=40000 -startLat=43.009449 -startLon=-74.006824 \ No newline at end of file +startLat=43.009339 +startLon=-74.009168 \ No newline at end of file diff --git a/src/test/java/SimpleGraphTests.java b/src/test/java/SimpleGraphTests.java index d6b55d7..fff91b2 100644 --- a/src/test/java/SimpleGraphTests.java +++ b/src/test/java/SimpleGraphTests.java @@ -1,6 +1,9 @@ import com.carrotsearch.hppc.IntDoubleHashMap; import com.carrotsearch.hppc.IntDoubleMap; -import com.graphhopper.routing.util.*; +import com.graphhopper.routing.util.EncodingManager; +import com.graphhopper.routing.util.FlagEncoder; +import com.graphhopper.routing.util.HintsMap; +import com.graphhopper.routing.util.RacingBikeFlagEncoder; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.GraphBuilder; import com.graphhopper.storage.GraphHopperStorage; @@ -9,7 +12,6 @@ import gurobi.GRBEnv; import gurobi.GRBException; import gurobi.GRBModel; -import io.github.plastix.BikePriorityWeighting; import io.github.plastix.Constraints; import io.github.plastix.GraphUtils; import io.github.plastix.Vars; @@ -71,14 +73,28 @@ private void runSolver(int startNode, int maxCost) throws GRBException { model.optimize(); } + @Test(expected = IllegalArgumentException.class) + public void emptyGraph() throws GRBException { + // Fails to run since we don't have a node ID (0) + runSolver(0, 1); + } + @Test - public void singleArcGraph() throws GRBException { + public void singleDirectedArcGraph() throws GRBException { addEdge(0, 1, false, 1, 1); runSolver(0, 2); assertNoSolution(); } + @Test + public void singleUndirectedArcGraph() throws GRBException { + addEdge(0, 1, true, 1, 1); + + runSolver(0, 2); + assertNoSolution(); + } + @Test public void disconnectedArcs() throws GRBException { addEdge(0, 1, false, 1, 1); @@ -96,7 +112,7 @@ public void singleDirectedThreeCycle() throws GRBException { runSolver(0, 3); assertHasSolution(); - assertSolution(3); + assertSolution(3, 3); } @Test @@ -107,7 +123,7 @@ public void singleUndirectedThreeCycle() throws GRBException { runSolver(0, 3); assertHasSolution(); - assertSolution(3); + assertSolution(3, 3); } @Test @@ -134,7 +150,7 @@ public void twoDisconnectedThreeCycles() throws GRBException { runSolver(0, 3); assertHasSolution(); - assertSolution(3); + assertSolution(3, 3); } @Test @@ -151,7 +167,7 @@ public void multipleDisconnectedThreeCycles() throws GRBException { runSolver(0, 3 * numThreeCycles); assertHasSolution(); - assertSolution(3); + assertSolution(3, 3); } @Test @@ -168,7 +184,7 @@ public void twoConnectedThreeCycles() throws GRBException { runSolver(0, 6); assertHasSolution(); - assertSolution(3); + assertSolution(3, 3); } @Test @@ -186,7 +202,7 @@ public void sixCycleWithTwoThreeCycles() throws GRBException { runSolver(0, 6); assertHasSolution(); - assertSolution(6); + assertSolution(6, 6); } @@ -201,7 +217,7 @@ public void directedKFour() throws GRBException { runSolver(0, 4); assertHasSolution(); - assertSolution(8); + assertSolution(8, 4); } @Test @@ -220,7 +236,7 @@ public void simpleDirectedCactusGraph() throws GRBException { runSolver(0, 6); assertHasSolution(); - assertSolution(8); + assertSolution(8, 6); } @Test @@ -233,7 +249,7 @@ public void threeNodeMultiEdgeGraph() throws GRBException { runSolver(0, 10); assertHasSolution(); - assertSolution(9); + assertSolution(9, 9); } private void assertHasSolution() throws GRBException { @@ -248,9 +264,9 @@ private void assertNoSolution() throws GRBException { } } - private void assertSolution(double score) throws GRBException { - double actualScore = model.get(GRB.DoubleAttr.ObjVal); - assertEquals(score, actualScore, FP_PRECISION); + private void assertSolution(double score, double cost) throws GRBException { + assertEquals(score, constraints.getObjective().getValue(), FP_PRECISION); + assertEquals(cost, constraints.getMaxCostConstraint().getValue(), FP_PRECISION); } private static class TestWeighting implements Weighting { From 72b28220fe89fc6306ad4c88a00086de45abfce1 Mon Sep 17 00:00:00 2001 From: Plastix Date: Fri, 2 Feb 2018 07:55:24 -0500 Subject: [PATCH 13/18] Print out solution distance and score --- src/main/java/io/github/plastix/Main.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/plastix/Main.java b/src/main/java/io/github/plastix/Main.java index 5cac274..4cd5dcb 100644 --- a/src/main/java/io/github/plastix/Main.java +++ b/src/main/java/io/github/plastix/Main.java @@ -7,6 +7,7 @@ import com.graphhopper.routing.util.FlagEncoder; import com.graphhopper.routing.weighting.Weighting; import com.graphhopper.storage.Graph; +import gurobi.GRB; import gurobi.GRBEnv; import gurobi.GRBException; import gurobi.GRBModel; @@ -24,13 +25,15 @@ public class Main { // Solver variables private static GRBEnv env; private static GRBModel model; + private static Vars vars; + private static Constraints constraints; private static void setupSolver() throws GRBException { env = new GRBEnv("osm.log"); model = new GRBModel(env); - Vars vars = new Vars(graph, model, graphUtils); + vars = new Vars(graph, model, graphUtils); + constraints = new Constraints(graph, model, vars, graphUtils); vars.addVarsToModel(); - Constraints constraints = new Constraints(graph, model, vars, graphUtils); constraints.setupConstraints(START_NODE_ID, params.getMaxCost()); } @@ -42,6 +45,11 @@ private static void runSolver() throws GRBException { // model.set(GRB.IntParam.LogToConsole, 0); model.optimize(); + if(model.get(GRB.IntAttr.Status) == GRB.Status.OPTIMAL) { + System.out.println("Route score: " + constraints.getObjective().getValue()); + System.out.println("Route distance: " + constraints.getMaxCostConstraint().getValue()); + } + model.dispose(); env.dispose(); hopper.close(); From 23728625721575fae299a8717a7015fe551c2a9d Mon Sep 17 00:00:00 2001 From: Plastix Date: Fri, 2 Feb 2018 11:08:04 -0500 Subject: [PATCH 14/18] Hopefully really fix the constraint --- .../io/github/plastix/SubtourConstraint.java | 58 ++++++++++++++----- src/main/java/io/github/plastix/Vars.java | 18 +++++- src/test/java/SimpleGraphTests.java | 27 +++++++-- 3 files changed, 82 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index 3965923..de30523 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -7,6 +7,8 @@ import com.graphhopper.util.EdgeIterator; import gurobi.*; +import java.util.Arrays; + public class SubtourConstraint extends GRBCallback { private final int START_NODE_ID; @@ -24,13 +26,19 @@ protected void callback() { try { if(where == GRB.CB_MIPSOL) { // Found an integer feasible solution + IntHashSet solutionVertices = getSolutionVertices(); IntHashSet visitedVertices = getReachableVertexSubset(START_NODE_ID); - int numVerticesInSolution = numVerticesInSolution(); + +// System.out.println("--- Callback ---"); +// System.out.println("Verts in solution: " + numVerticesInSolution); +// System.out.println(solutionVertices); +// System.out.println("Reachable vertices: " + visitedVertices.size()); +// printSolution(); // If the number of vertices we can reach from the start is not the number of vertices we // visit in the entire solution, we have a disconnected tour - if(visitedVertices.size() < numVerticesInSolution) { - visitedVertices.remove(START_NODE_ID); + if(visitedVertices.size() < solutionVertices.size()) { + solutionVertices.removeAll(visitedVertices); // Add sub-tour elimination constraint GRBLinExpr subtourConstraint = new GRBLinExpr(); @@ -38,24 +46,24 @@ protected void callback() { int totalOutgoingEdges = 0; double lhs = 0; - for(IntCursor cursor : visitedVertices) { + for(IntCursor cursor : solutionVertices) { int vertexId = cursor.value; EdgeIterator outgoing = graphUtils.outgoingEdges(vertexId); while(outgoing.next()) { GRBVar var = vars.getArcVar(outgoing, false); - if(getSolution(var) > 0) { + if(!solutionVertices.contains(outgoing.getAdjNode())) { subtourConstraint.addTerm(1, var); + lhs += getSolution(var); } totalOutgoingEdges += 1; - lhs += getSolution(var); } sumVertexVisits += getSolution(vars.getVertexVar(vertexId)); } double rhs = ((double) sumVertexVisits) / ((double) totalOutgoingEdges); - System.out.println("adding lazy constraint! " + lhs + " <= " + rhs); - addLazy(subtourConstraint, GRB.LESS_EQUAL, rhs); + System.out.println("adding lazy constraint! " + lhs + " >= " + rhs); + addLazy(subtourConstraint, GRB.GREATER_EQUAL, rhs); } } @@ -66,16 +74,17 @@ protected void callback() { } } - private int numVerticesInSolution() throws GRBException { - double[] values = getSolution(vars.getVertexVars()); + private IntHashSet getSolutionVertices() throws GRBException { + IntHashSet result = new IntHashSet(); + GRBVar[] verts = vars.getVertexVars(); + double[] values = getSolution(verts); - int visited = 0; - for(double value : values) { - if(value > 0) { - visited++; + for(int i = 0; i < verts.length; i++) { + if(values[i] > 0) { + result.add(i); } } - return visited; + return result; } private IntHashSet getReachableVertexSubset(int startNode) throws GRBException { @@ -101,4 +110,23 @@ private IntHashSet getReachableVertexSubset(int startNode) throws GRBException { return explored; } + + private void printSolution() throws GRBException { + GRBVar[] arcVars = vars.getArcVars(); + double[] values = getSolution(arcVars); + + StringBuilder arcString = new StringBuilder(); + + for(int i = 0; i < arcVars.length - 1; i++) { + arcString.append(values[i]); + arcString.append(", "); + arcString.append(arcVars[i].get(GRB.StringAttr.VarName)); + if(i < arcVars.length - 2) { + arcString.append("\n"); + } + } + System.out.println("Arcs: " + arcString.toString()); + double[] verts = getSolution(vars.getVertexVars()); + System.out.println("Verts: " + Arrays.toString(verts)); + } } diff --git a/src/main/java/io/github/plastix/Vars.java b/src/main/java/io/github/plastix/Vars.java index bc35d7c..aef978f 100644 --- a/src/main/java/io/github/plastix/Vars.java +++ b/src/main/java/io/github/plastix/Vars.java @@ -4,6 +4,7 @@ import com.carrotsearch.hppc.IntIntMap; import com.carrotsearch.hppc.IntObjectHashMap; import com.carrotsearch.hppc.IntObjectMap; +import com.carrotsearch.hppc.cursors.IntCursor; import com.graphhopper.routing.util.AllEdgesIterator; import com.graphhopper.storage.Graph; import com.graphhopper.util.EdgeIterator; @@ -50,11 +51,12 @@ public void addVarsToModel() throws GRBException { int edgeId = edges.getEdge(); int baseNode = edges.getBaseNode(); arcBaseIds.put(edgeId, baseNode); + int adjNode = edges.getAdjNode(); // Make a decision variable for every arc in our graph // arcs[i] = 1 if arc is travelled, 0 otherwise - GRBVar forward = model.addVar(0, 1, 0, GRB.BINARY, "forward_" + edgeId); - GRBVar backward = model.addVar(0, 1, 0, GRB.BINARY, "backward_" + edgeId); + GRBVar forward = model.addVar(0, 1, 0, GRB.BINARY, "forward_" + edgeId + "|" + baseNode + "->" + edges.getAdjNode()); + GRBVar backward = model.addVar(0, 1, 0, GRB.BINARY, "backward_" + edgeId + "|" + baseNode + "->" + edges.getAdjNode()); forwardArcs.put(edgeId, forward); backwardArcs.put(edgeId, backward); @@ -92,4 +94,16 @@ public GRBVar getVertexVar(int id) { public GRBVar[] getVertexVars() { return verts; } + + public GRBVar[] getArcVars() { + GRBVar[] result = new GRBVar[forwardArcs.values().size() * 2]; + + int j = 0; + for(IntCursor intCursor : forwardArcs.keys()) { + result[j++] = forwardArcs.get(intCursor.value); + result[j++] = backwardArcs.get(intCursor.value); + } + + return result; + } } diff --git a/src/test/java/SimpleGraphTests.java b/src/test/java/SimpleGraphTests.java index fff91b2..57f2c8e 100644 --- a/src/test/java/SimpleGraphTests.java +++ b/src/test/java/SimpleGraphTests.java @@ -8,10 +8,7 @@ import com.graphhopper.storage.GraphBuilder; import com.graphhopper.storage.GraphHopperStorage; import com.graphhopper.util.EdgeIteratorState; -import gurobi.GRB; -import gurobi.GRBEnv; -import gurobi.GRBException; -import gurobi.GRBModel; +import gurobi.*; import io.github.plastix.Constraints; import io.github.plastix.GraphUtils; import io.github.plastix.Vars; @@ -19,6 +16,8 @@ import org.junit.Before; import org.junit.Test; +import java.util.Arrays; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -202,6 +201,7 @@ public void sixCycleWithTwoThreeCycles() throws GRBException { runSolver(0, 6); assertHasSolution(); + printSolution(); assertSolution(6, 6); } @@ -252,6 +252,25 @@ public void threeNodeMultiEdgeGraph() throws GRBException { assertSolution(9, 9); } + private void printSolution() throws GRBException { + System.out.println("---- Final Solution ----"); + GRBVar[] arcVars = vars.getArcVars(); + double[] values = model.get(GRB.DoubleAttr.X, arcVars); + + StringBuilder arcString = new StringBuilder(); + + for(int i = 0; i < arcVars.length - 1; i++) { + arcString.append(values[i]); + arcString.append(", "); + arcString.append(arcVars[i].get(GRB.StringAttr.VarName)); + arcString.append("\n"); + } + System.out.println("Arcs: " + arcString.toString()); + double[] verts = model.get(GRB.DoubleAttr.X, vars.getVertexVars()); + System.out.println("Verts: " + Arrays.toString(verts)); + } + + private void assertHasSolution() throws GRBException { if(model.get(GRB.IntAttr.Status) != GRB.Status.OPTIMAL) { fail("Gurobi could not find an optimal solution!"); From ed28e3e900dc41e7661fb2bbf7ca4a56368e753d Mon Sep 17 00:00:00 2001 From: Plastix Date: Tue, 6 Feb 2018 16:15:35 -0500 Subject: [PATCH 15/18] Add simple lazy constraint profiling code --- .../io/github/plastix/SubtourConstraint.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index de30523..200fc54 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -8,12 +8,16 @@ import gurobi.*; import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import static gurobi.GRB.Callback.RUNTIME; public class SubtourConstraint extends GRBCallback { private final int START_NODE_ID; private GraphUtils graphUtils; private Vars vars; + private double time = 0; SubtourConstraint(Vars vars, int startNodeId, GraphUtils graphUtils) { this.vars = vars; @@ -25,7 +29,7 @@ public class SubtourConstraint extends GRBCallback { protected void callback() { try { if(where == GRB.CB_MIPSOL) { // Found an integer feasible solution - + long start = System.nanoTime(); IntHashSet solutionVertices = getSolutionVertices(); IntHashSet visitedVertices = getReachableVertexSubset(START_NODE_ID); @@ -62,10 +66,20 @@ protected void callback() { } double rhs = ((double) sumVertexVisits) / ((double) totalOutgoingEdges); - System.out.println("adding lazy constraint! " + lhs + " >= " + rhs); +// System.out.println("adding lazy constraint! " + lhs + " >= " + rhs); addLazy(subtourConstraint, GRB.GREATER_EQUAL, rhs); } + + long end = System.nanoTime(); + time += TimeUnit.NANOSECONDS.toSeconds(end - start); + + double solverTime = getDoubleInfo(RUNTIME); + if(solverTime > 600) { + System.out.println(String.format("Lazy constraint time: %f s", time)); + System.out.println(String.format("Gurobi wall time: %f s", solverTime)); + System.exit(0); + } } } catch(GRBException e) { System.out.println("Error code: " + e.getErrorCode() + ". " + From e0567858d2bd2e39d7667e799a99761fcaa7f371 Mon Sep 17 00:00:00 2001 From: Plastix Date: Tue, 6 Feb 2018 16:33:50 -0500 Subject: [PATCH 16/18] Fix time conversion --- src/main/java/io/github/plastix/SubtourConstraint.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index 200fc54..c117baa 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -17,7 +17,7 @@ public class SubtourConstraint extends GRBCallback { private final int START_NODE_ID; private GraphUtils graphUtils; private Vars vars; - private double time = 0; + private long time = 0; SubtourConstraint(Vars vars, int startNodeId, GraphUtils graphUtils) { this.vars = vars; @@ -72,11 +72,11 @@ protected void callback() { } long end = System.nanoTime(); - time += TimeUnit.NANOSECONDS.toSeconds(end - start); + time += end - start; double solverTime = getDoubleInfo(RUNTIME); if(solverTime > 600) { - System.out.println(String.format("Lazy constraint time: %f s", time)); + System.out.println(String.format("Lazy constraint time: %d s", TimeUnit.NANOSECONDS.toSeconds(time))); System.out.println(String.format("Gurobi wall time: %f s", solverTime)); System.exit(0); } From f7ae653265102e8fb0f24064f96cd568ef1219fb Mon Sep 17 00:00:00 2001 From: Plastix Date: Wed, 7 Feb 2018 07:11:59 -0500 Subject: [PATCH 17/18] Switch to getCurrentMillis --- src/main/java/io/github/plastix/SubtourConstraint.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index c117baa..676de8a 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -29,7 +29,7 @@ public class SubtourConstraint extends GRBCallback { protected void callback() { try { if(where == GRB.CB_MIPSOL) { // Found an integer feasible solution - long start = System.nanoTime(); + long start = System.currentTimeMillis(); IntHashSet solutionVertices = getSolutionVertices(); IntHashSet visitedVertices = getReachableVertexSubset(START_NODE_ID); @@ -71,12 +71,12 @@ protected void callback() { } - long end = System.nanoTime(); + long end = System.currentTimeMillis(); time += end - start; double solverTime = getDoubleInfo(RUNTIME); - if(solverTime > 600) { - System.out.println(String.format("Lazy constraint time: %d s", TimeUnit.NANOSECONDS.toSeconds(time))); + if(solverTime > 3600) { + System.out.println(String.format("Lazy constraint time: %d s", TimeUnit.MILLISECONDS.toSeconds(time))); System.out.println(String.format("Gurobi wall time: %f s", solverTime)); System.exit(0); } From ac32e81d066ea940b9dfe37c3b913d51b1206859 Mon Sep 17 00:00:00 2001 From: Plastix Date: Wed, 7 Feb 2018 09:40:05 -0500 Subject: [PATCH 18/18] More profiling changes --- .../java/io/github/plastix/SubtourConstraint.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/plastix/SubtourConstraint.java b/src/main/java/io/github/plastix/SubtourConstraint.java index 676de8a..95224a2 100644 --- a/src/main/java/io/github/plastix/SubtourConstraint.java +++ b/src/main/java/io/github/plastix/SubtourConstraint.java @@ -8,7 +8,6 @@ import gurobi.*; import java.util.Arrays; -import java.util.concurrent.TimeUnit; import static gurobi.GRB.Callback.RUNTIME; @@ -17,7 +16,7 @@ public class SubtourConstraint extends GRBCallback { private final int START_NODE_ID; private GraphUtils graphUtils; private Vars vars; - private long time = 0; + private double time = 0; SubtourConstraint(Vars vars, int startNodeId, GraphUtils graphUtils) { this.vars = vars; @@ -29,7 +28,7 @@ public class SubtourConstraint extends GRBCallback { protected void callback() { try { if(where == GRB.CB_MIPSOL) { // Found an integer feasible solution - long start = System.currentTimeMillis(); + long start = System.nanoTime(); IntHashSet solutionVertices = getSolutionVertices(); IntHashSet visitedVertices = getReachableVertexSubset(START_NODE_ID); @@ -71,12 +70,12 @@ protected void callback() { } - long end = System.currentTimeMillis(); - time += end - start; - + long end = System.nanoTime(); + double sec = (end - start) / 1000000000.0; + time += sec; double solverTime = getDoubleInfo(RUNTIME); if(solverTime > 3600) { - System.out.println(String.format("Lazy constraint time: %d s", TimeUnit.MILLISECONDS.toSeconds(time))); + System.out.println(String.format("Lazy constraint time: %f s", time)); System.out.println(String.format("Gurobi wall time: %f s", solverTime)); System.exit(0); }