From daa832b226d00c696d3447da98791066cf913402 Mon Sep 17 00:00:00 2001 From: J-morag Date: Thu, 26 Jan 2023 10:36:17 +0200 Subject: [PATCH] more tests --- .../BasicMAPF/Instances/Maps/MapFactory.java | 24 +++++-- .../UnitCostsAndManhattanDistance.java | 21 ++++++ .../AStar/SingleAgentAStar_Solver.java | 41 +++++------- .../DistanceTableAStarHeuristicTest.java | 13 ++++ .../AStar/SingleAgentAStar_SolverTest.java | 66 +++++++++++++++---- .../java/BasicMAPF/TestConstants/Maps.java | 9 ++- 6 files changed, 130 insertions(+), 44 deletions(-) create mode 100644 src/main/java/BasicMAPF/Solvers/AStar/CostsAndHeuristics/UnitCostsAndManhattanDistance.java diff --git a/src/main/java/BasicMAPF/Instances/Maps/MapFactory.java b/src/main/java/BasicMAPF/Instances/Maps/MapFactory.java index ed9d874f..de059b87 100644 --- a/src/main/java/BasicMAPF/Instances/Maps/MapFactory.java +++ b/src/main/java/BasicMAPF/Instances/Maps/MapFactory.java @@ -3,10 +3,7 @@ import BasicMAPF.Instances.Maps.Coordinates.Coordinate_2D; import BasicMAPF.Instances.Maps.Coordinates.I_Coordinate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Responsible for the creation of instances of all classes that implement {@link I_Map}. @@ -16,7 +13,6 @@ public class MapFactory { /** * Generates a new 4-connected {@link GraphMap} from a square, 2D grid. - * * Simple - Only 2 {@link Enum_MapLocationType location types} exist, {@link Enum_MapLocationType#EMPTY} and * {@link Enum_MapLocationType#WALL}. {@link Enum_MapLocationType#EMPTY} locations are passable, and can only connect to other * {@link Enum_MapLocationType#EMPTY} locations. {@link Enum_MapLocationType#WALL} locations are impassable, and can not connect to @@ -24,9 +20,10 @@ public class MapFactory { * @param rectangle_2D_Map A rectangle grid representing a map, containing only {@link Enum_MapLocationType#EMPTY} and * {@link Enum_MapLocationType#WALL}. The length of its first dimension should correspond to the * original map's x dimension. + * @param isOneConnectedComponent if the grid is one connected component. * @return a new 4-connected {@link GraphMap}. */ - public static GraphMap newSimple4Connected2D_GraphMap(Enum_MapLocationType[][] rectangle_2D_Map){ + public static GraphMap newSimple4Connected2D_GraphMap(Enum_MapLocationType[][] rectangle_2D_Map, boolean isOneConnectedComponent){ int xAxis_length = rectangle_2D_Map.length; int yAxis_length = rectangle_2D_Map[0].length; GraphMapVertex[][] locations = new GraphMapVertex[xAxis_length][yAxis_length]; //rectangle map @@ -64,6 +61,21 @@ public static GraphMap newSimple4Connected2D_GraphMap(Enum_MapLocationType[][] r return new GraphMap(allLocations); } + /** + * Generates a new 4-connected {@link GraphMap} from a square, 2D grid. + * Simple - Only 2 {@link Enum_MapLocationType location types} exist, {@link Enum_MapLocationType#EMPTY} and + * {@link Enum_MapLocationType#WALL}. {@link Enum_MapLocationType#EMPTY} locations are passable, and can only connect to other + * {@link Enum_MapLocationType#EMPTY} locations. {@link Enum_MapLocationType#WALL} locations are impassable, and can not connect to + * any other location, so they will not be generated. + * @param rectangle_2D_Map A rectangle grid representing a map, containing only {@link Enum_MapLocationType#EMPTY} and + * {@link Enum_MapLocationType#WALL}. The length of its first dimension should correspond to the + * original map's x dimension. + * @return a new 4-connected {@link GraphMap}. + */ + public static GraphMap newSimple4Connected2D_GraphMap(Enum_MapLocationType[][] rectangle_2D_Map){ + return newSimple4Connected2D_GraphMap(rectangle_2D_Map, true); + } + /* nicetohave - 8 connected 2D map public static GraphMap newSimple8Connected2D_GraphMap(Enum_MapLocationType[][] map_2D){ return null; diff --git a/src/main/java/BasicMAPF/Solvers/AStar/CostsAndHeuristics/UnitCostsAndManhattanDistance.java b/src/main/java/BasicMAPF/Solvers/AStar/CostsAndHeuristics/UnitCostsAndManhattanDistance.java new file mode 100644 index 00000000..3b142477 --- /dev/null +++ b/src/main/java/BasicMAPF/Solvers/AStar/CostsAndHeuristics/UnitCostsAndManhattanDistance.java @@ -0,0 +1,21 @@ +package BasicMAPF.Solvers.AStar.CostsAndHeuristics; + +import BasicMAPF.Instances.Maps.Coordinates.I_Coordinate; +import BasicMAPF.Solvers.AStar.SingleAgentAStar_Solver; + +public class UnitCostsAndManhattanDistance implements AStarGAndH { + private final I_Coordinate target; + + public UnitCostsAndManhattanDistance(I_Coordinate target) { + this.target = target; + } + + @Override + public float getH(SingleAgentAStar_Solver.AStarState state) { + return state.move.currLocation.getCoordinate().distance(target); + } + @Override + public boolean isConsistent() { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/BasicMAPF/Solvers/AStar/SingleAgentAStar_Solver.java b/src/main/java/BasicMAPF/Solvers/AStar/SingleAgentAStar_Solver.java index 2260c440..4f9df011 100644 --- a/src/main/java/BasicMAPF/Solvers/AStar/SingleAgentAStar_Solver.java +++ b/src/main/java/BasicMAPF/Solvers/AStar/SingleAgentAStar_Solver.java @@ -7,6 +7,7 @@ import BasicMAPF.Instances.Maps.I_Map; import BasicMAPF.Instances.Maps.I_Location; import BasicMAPF.Solvers.AStar.CostsAndHeuristics.AStarGAndH; +import BasicMAPF.Solvers.AStar.CostsAndHeuristics.UnitCostsAndManhattanDistance; import BasicMAPF.Solvers.ConstraintsAndConflicts.ConflictManagement.I_ConflictAvoidanceTable; import Environment.Metrics.InstanceReport; import BasicMAPF.Solvers.*; @@ -84,26 +85,17 @@ protected void init(MAPF_Instance instance, RunParameters runParameters){ this.existingSolution.putPlan(this.existingPlan); } - if(runParameters instanceof RunParameters_SAAStar parameters - && ((RunParameters_SAAStar) runParameters).heuristicFunction != null){ - this.gAndH = parameters.heuristicFunction; - } - else{ - this.gAndH = new defaultGAndH(); - } - if (! this.gAndH.isConsistent()){ - throw new IllegalArgumentException("Support for inconsistent heuristic is not implemented."); - } - if(runParameters instanceof RunParameters_SAAStar parameters && ((RunParameters_SAAStar) runParameters).problemStartTime >= 0){ this.problemStartTime = parameters.problemStartTime; } + if(runParameters instanceof RunParameters_SAAStar parameters && ((RunParameters_SAAStar) runParameters).conflictAvoidanceTable != null){ this.conflictAvoidanceTable = parameters.conflictAvoidanceTable; } // else keep the value that it has already been initialised with (above) + if(runParameters instanceof RunParameters_SAAStar parameters){ this.sourceCoor = parameters.sourceCoor != null ? parameters.sourceCoor : agent.source; this.targetCoor = parameters.targetCoor != null ? parameters.targetCoor : agent.target; @@ -112,6 +104,18 @@ protected void init(MAPF_Instance instance, RunParameters runParameters){ this.sourceCoor = agent.source; this.targetCoor = agent.target; } + + if(runParameters instanceof RunParameters_SAAStar parameters + && ((RunParameters_SAAStar) runParameters).heuristicFunction != null){ + this.gAndH = parameters.heuristicFunction; + } + else{ + this.gAndH = new UnitCostsAndManhattanDistance(this.targetCoor); + } + if (! this.gAndH.isConsistent()){ + throw new IllegalArgumentException("Support for inconsistent heuristic is not implemented."); + } + if(runParameters instanceof RunParameters_SAAStar parameters){ this.fBudget = parameters.fBudget; } @@ -119,8 +123,6 @@ protected void init(MAPF_Instance instance, RunParameters runParameters){ this.fBudget = Float.POSITIVE_INFINITY; } -// this.openList = new OpenList<>(stateFComparator); -// this.closed = new HashSet<>(); this.expandedNodes = 0; this.generatedNodes = 0; } @@ -262,7 +264,7 @@ public class AStarState implements Comparable{ * is to say, if all tie breaking methods still result in equality, tie break for using serialID. */ private final int serialID = SingleAgentAStar_Solver.this.generatedNodes++; // take and increment - private final Move move; + public final Move move; private final AStarState prev; private final int g; private final float h; @@ -436,17 +438,6 @@ public String toString() { } ////////// end AStarState - private class defaultGAndH implements AStarGAndH { - @Override - public float getH(AStarState state) { - return state.move.currLocation.getCoordinate().distance(SingleAgentAStar_Solver.this.targetCoor); - } - @Override - public boolean isConsistent() { - return true; - } - } - /** * For sorting the open list. diff --git a/src/test/java/BasicMAPF/Solvers/AStar/DistanceTableAStarHeuristicTest.java b/src/test/java/BasicMAPF/Solvers/AStar/DistanceTableAStarHeuristicTest.java index 6ead11d5..468016cd 100644 --- a/src/test/java/BasicMAPF/Solvers/AStar/DistanceTableAStarHeuristicTest.java +++ b/src/test/java/BasicMAPF/Solvers/AStar/DistanceTableAStarHeuristicTest.java @@ -1,13 +1,17 @@ package BasicMAPF.Solvers.AStar; import BasicMAPF.Instances.Agent; +import BasicMAPF.Instances.MAPF_Instance; import BasicMAPF.Instances.Maps.*; import BasicMAPF.Instances.Maps.Coordinates.Coordinate_2D; import BasicMAPF.Instances.Maps.Coordinates.I_Coordinate; import BasicMAPF.Solvers.AStar.CostsAndHeuristics.DistanceTableAStarHeuristic; +import Environment.Metrics.InstanceReport; import org.junit.jupiter.api.Test; +import static BasicMAPF.TestConstants.Agents.agent04to00; import static BasicMAPF.TestConstants.Maps.mapH; +import static BasicMAPF.TestConstants.Maps.mapWithPocket; import static org.junit.jupiter.api.Assertions.*; import java.util.HashMap; @@ -112,4 +116,13 @@ public void test(){ assertTrue(equalsAllAgentMap(expected, distanceTableAStarHeuristic.getDistanceDictionaries())); } + @Test + void failIfMapIsNotOneConnectedComponent(){ + MAPF_Instance testInstance = new MAPF_Instance("pocket", mapWithPocket, new Agent[]{agent04to00}); + + DistanceTableAStarHeuristic distanceTableAStarHeuristic = new DistanceTableAStarHeuristic(testInstance.agents, testInstance.map); + SingleAgentAStar_Solver solver = new SingleAgentAStar_Solver(); + assertThrows(IllegalArgumentException.class, () -> solver.solve(testInstance, new RunParameters_SAAStar(new InstanceReport(), distanceTableAStarHeuristic))); + } + } \ No newline at end of file diff --git a/src/test/java/BasicMAPF/Solvers/AStar/SingleAgentAStar_SolverTest.java b/src/test/java/BasicMAPF/Solvers/AStar/SingleAgentAStar_SolverTest.java index 4972be03..21cc239a 100644 --- a/src/test/java/BasicMAPF/Solvers/AStar/SingleAgentAStar_SolverTest.java +++ b/src/test/java/BasicMAPF/Solvers/AStar/SingleAgentAStar_SolverTest.java @@ -2,6 +2,8 @@ import BasicMAPF.Instances.Maps.Coordinates.Coordinate_2D; import BasicMAPF.Solvers.AStar.CostsAndHeuristics.AStarGAndH; +import BasicMAPF.Solvers.AStar.CostsAndHeuristics.DistanceTableAStarHeuristic; +import BasicMAPF.Solvers.AStar.CostsAndHeuristics.UnitCostsAndManhattanDistance; import Environment.IO_Package.IO_Manager; import BasicMAPF.Instances.Agent; import BasicMAPF.Instances.InstanceBuilders.InstanceBuilder_BGU; @@ -382,7 +384,7 @@ public String toString() { } } - private AStarGAndH unitCostAndNoHeuristic = new UnitCostAndNoHeuristic(); + private final AStarGAndH unitCostAndNoHeuristic = new UnitCostAndNoHeuristic(); private static List planLocations(SingleAgentPlan planFromAStar) { List aStarPlanLocations = new ArrayList<>(); @@ -427,8 +429,9 @@ void optimalVsUCS4(){ } @Test void optimalVsUCSDynamic(){ + Map maps = singleStronglyConnectedComponentMapsWithNames; for (I_ExplicitMap testMap : - stronglyConnectedComponentMapsWithNames.keySet()) { + maps.keySet()) { for (I_Location source : testMap.getAllLocations()) { for (I_Location target : @@ -436,7 +439,7 @@ void optimalVsUCSDynamic(){ if ( ! source.equals(target)){ Agent agent = new Agent(0, source.getCoordinate(), target.getCoordinate()); MAPF_Instance testInstance = new MAPF_Instance( - stronglyConnectedComponentMapsWithNames.get(testMap) + " " + agent, testMap, new Agent[]{agent}); + maps.get(testMap) + " " + agent, testMap, new Agent[]{agent}); compareAStarAndUCS(aStar, new InstanceReport(), agent, testInstance, unitCostAndNoHeuristic); } @@ -444,8 +447,48 @@ void optimalVsUCSDynamic(){ } } } + @Test + void optimalVsUCSDynamicWithDistanceTableHeuristic(){ + Map maps = singleStronglyConnectedComponentMapsWithNames; + for (I_ExplicitMap testMap : + maps.keySet()) { + for (I_Location source : + testMap.getAllLocations()) { + for (I_Location target : + testMap.getAllLocations()) { + if ( ! source.equals(target)){ + Agent agent = new Agent(0, source.getCoordinate(), target.getCoordinate()); + MAPF_Instance testInstance = new MAPF_Instance( + maps.get(testMap) + " " + agent, testMap, new Agent[]{agent}); + DistanceTableAStarHeuristic distanceTableAStarHeuristic = new DistanceTableAStarHeuristic(testInstance.agents, testInstance.map); + compareAStarAndUCS(aStar, new InstanceReport(), + agent, testInstance, distanceTableAStarHeuristic); + } + } + } + } + } + @Test + void optimalVsUCSDDynamicWithManhattanDistanceHeuristic(){ + Map maps = singleStronglyConnectedComponentGridMapsWithNames; // grid maps only! + for (I_ExplicitMap testMap : + maps.keySet()) { + for (I_Location source : + testMap.getAllLocations()) { + for (I_Location target : + testMap.getAllLocations()) { + if ( ! source.equals(target)){ + Agent agent = new Agent(0, source.getCoordinate(), target.getCoordinate()); + MAPF_Instance testInstance = new MAPF_Instance( + maps.get(testMap) + " " + agent, testMap, new Agent[]{agent}); + compareAStarAndUCS(aStar, new InstanceReport(), agent, testInstance, new UnitCostsAndManhattanDistance(agent.target)); + } + } + } + } + } - private class RandomButStableCostsFrom1To10AndNoHeuristic implements AStarGAndH{ + private static class RandomButStableCostsFrom1To10AndNoHeuristic implements AStarGAndH{ Map randomButStableCosts = new HashMap<>(); Random rand; @@ -482,7 +525,7 @@ public String toString() { void optimalVsUCSWeightedEdges1(){ MAPF_Instance testInstance = instanceMaze1; Agent agent = testInstance.agents.get(0); - AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long)(agent.hashCode())); + AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long) (agent.hashCode())); compareAStarAndUCS(aStar, instanceReport, agent, testInstance, randomStableCosts); } @@ -490,7 +533,7 @@ void optimalVsUCSWeightedEdges1(){ void optimalVsUCSWeightedEdges2(){ MAPF_Instance testInstance = instanceMaze2; Agent agent = testInstance.agents.get(0); - AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long)(agent.hashCode())); + AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long) (agent.hashCode())); compareAStarAndUCS(aStar, instanceReport, agent, testInstance, randomStableCosts); } @@ -498,7 +541,7 @@ void optimalVsUCSWeightedEdges2(){ void optimalVsUCSWeightedEdges3(){ MAPF_Instance testInstance = instanceMaze3; Agent agent = testInstance.agents.get(0); - AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long)(agent.hashCode())); + AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long) (agent.hashCode())); compareAStarAndUCS(aStar, instanceReport, agent, testInstance, randomStableCosts); } @@ -506,14 +549,15 @@ void optimalVsUCSWeightedEdges3(){ void optimalVsUCSWeightedEdges4(){ MAPF_Instance testInstance = instanceMaze4; Agent agent = testInstance.agents.get(0); - AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long)(agent.hashCode())); + AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long) (agent.hashCode())); compareAStarAndUCS(aStar, instanceReport, agent, testInstance, randomStableCosts); } @Test void optimalVsUCSWeightedEdgesDynamic(){ + Map maps = singleStronglyConnectedComponentMapsWithNames; for (I_ExplicitMap testMap : - stronglyConnectedComponentMapsWithNames.keySet()) { + maps.keySet()) { for (I_Location source : testMap.getAllLocations()) { for (I_Location target : @@ -521,8 +565,8 @@ void optimalVsUCSWeightedEdgesDynamic(){ if ( ! source.equals(target)){ Agent agent = new Agent(0, source.getCoordinate(), target.getCoordinate()); MAPF_Instance testInstance = new MAPF_Instance( - stronglyConnectedComponentMapsWithNames.get(testMap) + " " + agent, testMap, new Agent[]{agent}); - AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long)(agent.hashCode())); + maps.get(testMap) + " " + agent, testMap, new Agent[]{agent}); + AStarGAndH randomStableCosts = new RandomButStableCostsFrom1To10AndNoHeuristic((long) (agent.hashCode())); compareAStarAndUCS(aStar, new InstanceReport(), agent, testInstance, randomStableCosts); } } diff --git a/src/test/java/BasicMAPF/TestConstants/Maps.java b/src/test/java/BasicMAPF/TestConstants/Maps.java index 91347019..6b6c5157 100644 --- a/src/test/java/BasicMAPF/TestConstants/Maps.java +++ b/src/test/java/BasicMAPF/TestConstants/Maps.java @@ -37,7 +37,7 @@ public class Maps { {e, e, w, e, w, w}, {w, e, w, e, e, e}, }; - public static final I_ExplicitMap mapWithPocket = MapFactory.newSimple4Connected2D_GraphMap(map_2D_withPocket); + public static final I_ExplicitMap mapWithPocket = MapFactory.newSimple4Connected2D_GraphMap(map_2D_withPocket, false); public static final Enum_MapLocationType[][] map_2D_smallMaze = { {e, e, e, w, e, w}, @@ -141,10 +141,15 @@ private static I_ExplicitMap createRandomStronglyConnectedGraphMap(int seed, int return MapFactory.newArbitraryGraphMap(coordinatesAdjacencyLists, coordinatesEdgeWeights, coordinatesLocationTypes, true); } - public static final Map stronglyConnectedComponentMapsWithNames = Map.of( + public static final Map singleStronglyConnectedComponentMapsWithNames = Map.of( mapCircle, "mapCircle", mapEmpty, "mapEmpty", mapSmallMaze, "mapSmallMaze", mapH, "mapH", mapHLong, "mapHLong", randomArbitraryGraphMap1, "randomArbitraryGraphMap1", randomArbitraryGraphMap2, "randomArbitraryGraphMap2", randomArbitraryGraphMap3, "randomArbitraryGraphMap3", randomArbitraryGraphMap4, "randomArbitraryGraphMap4", randomArbitraryGraphMap5, "randomArbitraryGraphMap5" ); + + public static final Map singleStronglyConnectedComponentGridMapsWithNames = Map.of( + mapCircle, "mapCircle", mapEmpty, "mapEmpty", mapSmallMaze, "mapSmallMaze", mapH, "mapH", + mapHLong, "mapHLong" + ); }