diff --git a/src/main/java/BasicCBS/Instances/Maps/GraphMap.java b/src/main/java/BasicCBS/Instances/Maps/GraphMap.java index c03625cb..51682619 100644 --- a/src/main/java/BasicCBS/Instances/Maps/GraphMap.java +++ b/src/main/java/BasicCBS/Instances/Maps/GraphMap.java @@ -3,7 +3,6 @@ import BasicCBS.Instances.Maps.Coordinates.I_Coordinate; import java.util.*; -import java.util.function.Consumer; /** * Represents a {@link I_Map map} as an abstract graph. This implementation can, in principle, support any domain - @@ -59,7 +58,7 @@ public I_Map getSubmapWithout(Collection mapLocations) { if(!mapLocations.contains(originalVertex)){ GraphMapVertex newVertex = vertexMappings.get(coor); List neighbors = new ArrayList<>(); - for (I_Location neighbor : originalVertex.neighbors) { + for (I_Location neighbor : originalVertex.outgoingEdges) { if (! mapLocations.contains(neighbor)){ // getting the neighbor from the new vertices, not the original ones. neighbors.add(vertexMappings.get(neighbor.getCoordinate())); diff --git a/src/main/java/BasicCBS/Instances/Maps/GraphMapVertex.java b/src/main/java/BasicCBS/Instances/Maps/GraphMapVertex.java index d5fe0b1a..d5944c46 100644 --- a/src/main/java/BasicCBS/Instances/Maps/GraphMapVertex.java +++ b/src/main/java/BasicCBS/Instances/Maps/GraphMapVertex.java @@ -1,11 +1,14 @@ package BasicCBS.Instances.Maps; import BasicCBS.Instances.Maps.Coordinates.I_Coordinate; +import org.apache.commons.collections4.list.UnmodifiableList; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** - * A single cell in a {@link GraphMap}. Represents a unique location in the graph. + * A single cell(/vertex) in a {@link GraphMap}. Represents a unique location in the graph. * Immutable beyond first initialization (First with a constructor, and then with {@link #setNeighbors(GraphMapVertex[])}. */ public class GraphMapVertex implements I_Location { @@ -20,23 +23,62 @@ public class GraphMapVertex implements I_Location { * The type of the cell. The type could determine whether or not an agent can traverse or occupy a cell. */ public final Enum_MapCellType cellType; - public List neighbors; + public List outgoingEdges; + private List incomingEdges = new ArrayList<>(); + /** + * weights for the edges connecting this location to its neighbors. indexed uniformly with {@link #outgoingEdges}. + * If weights were not provided, contains a uniform edge cost of 1. + */ + public List outgoingEdgeWeights; + /** + * weights for the edges connecting neighbors to this location. indexed uniformly with {@link #outgoingEdges}. + * If weights were not provided, contains a uniform edge cost of 1. + */ + public List incomingEdgeWeights = new ArrayList<>(); + public final I_Coordinate coordinate; GraphMapVertex(Enum_MapCellType cellType, I_Coordinate coordinate) { this.cellType = cellType; this.coordinate = coordinate; - this.neighbors = null; + this.outgoingEdges = null; } /** * Sets the cell's neighbors. All cells in the array should logically be non null. * Used during graph construction. Only the first call to this method on an instance affects the instance. - * @param neighbors the cell's neighbors. + * Also sets this as a reverse edge on each of the neighbors. + * @param outgoingEdges the cell's neighbors. * @throws NullPointerException if an element is null or the given array is null. */ - void setNeighbors(GraphMapVertex[] neighbors) { - this.neighbors = (this.neighbors == null ? List.of(neighbors) : this.neighbors); + void setNeighbors(GraphMapVertex[] outgoingEdges) { + Integer[] edgeWeights = new Integer[outgoingEdges.length]; + Arrays.fill(edgeWeights, 1); + setNeighbors(outgoingEdges, edgeWeights); + } + + /** + * Sets the cell's neighbors. All cells in the array should logically be non null. + * Used during graph construction. Only the first call to this method on an instance affects the instance. + * Also sets this as a reverse edge on each of the neighbors. + * Also sets weights for the edges to the neighbors. + * @param outgoingEdges the cell's neighbors. must be indexed uniformly with edgeWeights. + * @param edgeWeights weights of the connections to the neighbors. must be indexed uniformly with neighbors. + * @throws NullPointerException if an element is null or the given array is null. + */ + void setNeighbors(GraphMapVertex[] outgoingEdges, Integer[] edgeWeights) { + // unmodifiable list + if (outgoingEdges.length != edgeWeights.length){ + throw new IllegalArgumentException("neighbors and edgeWeights must be indexed uniformly"); + } + this.outgoingEdges = (this.outgoingEdges == null ? List.of(outgoingEdges) : this.outgoingEdges); + this.outgoingEdgeWeights = (this.outgoingEdgeWeights == null ? List.of(edgeWeights) : this.outgoingEdgeWeights); + // set reverse edges + for (int i = 0; i < outgoingEdges.length; i++) { + GraphMapVertex neighbor = outgoingEdges[i]; + neighbor.incomingEdges.add(this); + neighbor.incomingEdgeWeights.add(edgeWeights[i]); + } } /** @@ -64,20 +106,50 @@ public I_Coordinate getCoordinate() { * @return an UnmodifiableList of this cell's neighbors. */ @Override - public List getNeighbors() { - return this.neighbors; + public List outgoingEdges() { + return this.outgoingEdges; + } + + /** + * {@inheritDoc} + * Also locks the internal list of incoming agents from further modification. + * @return {@inheritDoc} + */ + @Override + public List incomingEdges() { + if (! (this.incomingEdges instanceof UnmodifiableList) ){ + this.incomingEdges = List.of(this.incomingEdges.toArray(I_Location[]::new)); + } + return this.incomingEdges; + } + + /** + * Returns an UnmodifiableList of the weights of this cell's edges. + * The amount of neighbors varies by map and connectivity. + * Runs in O(1). + * Indexed uniformly with the list of neighbors returned by {@link #outgoingEdges()}. + * @return an UnmodifiableList of the weights of this cell's edges. + */ + @Override + public List getOutgoingEdgesWeights() { + return this.outgoingEdgeWeights; + } + + @Override + public List getIncomingEdgesWeights() { + return null; } /** - * Returns true iff other is contained in {@link #neighbors}. In particular, returns false if other==this. + * Returns true iff other is contained in {@link #outgoingEdges}. In particular, returns false if other==this. * @param other another {@link I_Location}. - * @return true iff other is contained in {@link #neighbors}. + * @return true iff other is contained in {@link #outgoingEdges}. */ @Override public boolean isNeighbor(I_Location other) { boolean result = false; for (I_Location neighbor : - neighbors) { + outgoingEdges) { result = result || (neighbor.equals(other)); } return result; diff --git a/src/main/java/BasicCBS/Instances/Maps/I_Location.java b/src/main/java/BasicCBS/Instances/Maps/I_Location.java index 60463e3d..4e80f4cc 100644 --- a/src/main/java/BasicCBS/Instances/Maps/I_Location.java +++ b/src/main/java/BasicCBS/Instances/Maps/I_Location.java @@ -13,11 +13,18 @@ public interface I_Location { Enum_MapCellType getType(); /** - * Returns an array that contains references to this cell's neighbors. Should not include this. + * Returns an array that contains references to locations directly reachable from this. Should not include this. * The amount of neighbors varies by map and connectivity. - * @return an array that contains references to this cell's neighbors. Should not include this. + * @return an array that contains references to locations directly reachable from this. Should not include this. */ - List getNeighbors(); + List outgoingEdges(); + + /** + * Returns an array of locations from which this location is directly reachable. Should not include this. + * The amount of neighbors varies by map and connectivity. + * @return an array of locations from which this location is directly reachable. Should not include this. + */ + List incomingEdges(); /** * returns the cell's coordinate. @@ -25,6 +32,20 @@ public interface I_Location { */ I_Coordinate getCoordinate(); + /** + * Get weights of the connections to this cell's neighbors. + * Should be uniformly indexed with the return value of {@link #outgoingEdges()}. + * @return weights of the connections to this cell's neighbors. + */ + List getOutgoingEdgesWeights(); + + /** + * Get weights of the connections from this cell's neighbors. + * Should be uniformly indexed with the return value of {@link #incomingEdges()}. + * @return weights of the connections from this cell's neighbors. + */ + List getIncomingEdgesWeights(); + /** * Return true iff other is a neighbor of this. * @param other another {@link I_Location}. diff --git a/src/main/java/BasicCBS/Solvers/AStar/DistanceTableAStarHeuristic.java b/src/main/java/BasicCBS/Solvers/AStar/DistanceTableAStarHeuristic.java index c3f47895..d1d2340d 100644 --- a/src/main/java/BasicCBS/Solvers/AStar/DistanceTableAStarHeuristic.java +++ b/src/main/java/BasicCBS/Solvers/AStar/DistanceTableAStarHeuristic.java @@ -62,11 +62,11 @@ public void addTargetToHeuristic(I_Location target) { Map mapForAgent = new HashMap<>(); this.distanceDictionaries.put(target, mapForAgent); - //distance of a graphMapCell from itself + //distance of a vertex from itself this.distanceDictionaries.get(target).put(target, 0); - //all the neighbors of a graphMapCell - List neighbors = target.getNeighbors(); + // reverse edges, moving back from the target, traversing against the direction of the edges + List neighbors = target.incomingEdges(); LinkedList queue = new LinkedList<>(neighbors); int distance = 1; @@ -79,8 +79,8 @@ public void addTargetToHeuristic(I_Location target) { if (!(this.distanceDictionaries.get(target).containsKey(i_location))) { this.distanceDictionaries.get(target).put(i_location, distance); - //add all the neighbors of the current graphMapCell to the queue - List neighborsCell = i_location.getNeighbors(); + // traversing edges in reverse back from current vertex + List neighborsCell = i_location.incomingEdges(); queue.addAll(neighborsCell); } diff --git a/src/main/java/BasicCBS/Solvers/AStar/SingleAgentAStar_Solver.java b/src/main/java/BasicCBS/Solvers/AStar/SingleAgentAStar_Solver.java index 2f1231b4..e7804265 100644 --- a/src/main/java/BasicCBS/Solvers/AStar/SingleAgentAStar_Solver.java +++ b/src/main/java/BasicCBS/Solvers/AStar/SingleAgentAStar_Solver.java @@ -183,8 +183,8 @@ protected boolean initOpen() { else { // the existing plan is empty (no existing plan) I_Location sourceCell = map.getMapCell(this.sourceCoor); - // can move to neighboring cells or stay put - List neighborCellsIncludingCurrent = new ArrayList<>(sourceCell.getNeighbors()); + // can move to neighboring locations or stay put + List neighborCellsIncludingCurrent = new ArrayList<>(sourceCell.outgoingEdges()); neighborCellsIncludingCurrent.add(sourceCell); for (I_Location destination: neighborCellsIncludingCurrent) { @@ -300,7 +300,7 @@ private float calcH() { public void expand() { expandedNodes++; // can move to neighboring cells or stay put - List neighborCellsIncludingCurrent = new ArrayList<>(this.move.currLocation.getNeighbors()); + List neighborCellsIncludingCurrent = new ArrayList<>(this.move.currLocation.outgoingEdges()); neighborCellsIncludingCurrent.add(this.move.currLocation); for (I_Location destination: neighborCellsIncludingCurrent){ diff --git a/src/main/java/BasicCBS/Solvers/ConstraintsAndConflicts/ConflictManagement/CorridorConflictManager.java b/src/main/java/BasicCBS/Solvers/ConstraintsAndConflicts/ConflictManagement/CorridorConflictManager.java index 93041b62..975bb102 100644 --- a/src/main/java/BasicCBS/Solvers/ConstraintsAndConflicts/ConflictManagement/CorridorConflictManager.java +++ b/src/main/java/BasicCBS/Solvers/ConstraintsAndConflicts/ConflictManagement/CorridorConflictManager.java @@ -9,7 +9,6 @@ import BasicCBS.Solvers.ConstraintsAndConflicts.CorridorConflict; import BasicCBS.Solvers.ConstraintsAndConflicts.SwappingConflict; import BasicCBS.Solvers.Move; -import BasicCBS.Solvers.SingleAgentPlan; import java.util.HashSet; import java.util.Objects; @@ -123,8 +122,8 @@ private I_Location exploreCorridorOneDirection(HashSet corridorVerti // might add an existing vertex but it's fine since this is a set. corridorVertices.add(someDirNeighbor); // get the neighbor of the current neighbor that is not the anchor (so it is in the right direction) - I_Location newNeighbour = someDirNeighbor.getNeighbors().get(0).equals(anchor) ? someDirNeighbor.getNeighbors().get(1) - : someDirNeighbor.getNeighbors().get(0); + I_Location newNeighbour = someDirNeighbor.outgoingEdges().get(0).equals(anchor) ? someDirNeighbor.outgoingEdges().get(1) + : someDirNeighbor.outgoingEdges().get(0); anchor = someDirNeighbor; someDirNeighbor = newNeighbour; } @@ -138,7 +137,7 @@ private boolean equalsSourceOrTarget(I_Location location, Agent agent){ } private boolean isDegree2(I_Location location){ - return location.getNeighbors().size() == 2; + return location.outgoingEdges().size() == 2; } @Override diff --git a/src/main/java/BasicCBS/Solvers/ICTS/MDDs/MDDSearchNode.java b/src/main/java/BasicCBS/Solvers/ICTS/MDDs/MDDSearchNode.java index 27f84346..238181d9 100644 --- a/src/main/java/BasicCBS/Solvers/ICTS/MDDs/MDDSearchNode.java +++ b/src/main/java/BasicCBS/Solvers/ICTS/MDDs/MDDSearchNode.java @@ -88,7 +88,7 @@ public void addParents(List parents) { public List getNeighborLocations(){ // can move to neighboring cells or stay put I_Location currLocation = this.location; - List neighborCellsIncludingCurrent = new ArrayList<>(currLocation.getNeighbors()); + List neighborCellsIncludingCurrent = new ArrayList<>(currLocation.outgoingEdges()); neighborCellsIncludingCurrent.add(currLocation); //staying in the same location is possible return neighborCellsIncludingCurrent; } diff --git a/src/main/java/BasicCBS/Solvers/NoStateTimeSearches.java b/src/main/java/BasicCBS/Solvers/NoStateTimeSearches.java index 4cb285ab..734c78d5 100644 --- a/src/main/java/BasicCBS/Solvers/NoStateTimeSearches.java +++ b/src/main/java/BasicCBS/Solvers/NoStateTimeSearches.java @@ -51,7 +51,7 @@ private static boolean basicSearch(I_Location goal, I_Location source, Queue