Skip to content

Commit

Permalink
API improvements.
Browse files Browse the repository at this point in the history
* Added AbortBeforeIfModule
* doConsiderEdgeForRelaxiation for DijkstraModules exposes TentativeDistance as additional argument
* SimpleGraph and SimpleEdge are not final anymore
* Path can now be iterated reversely via reverseIterator()
* Added 2 shortestPathReachable methods which return a PathTree for more complex path retrieval
  • Loading branch information
Zabuzard committed Nov 7, 2019
1 parent 2be81a1 commit c8315f6
Show file tree
Hide file tree
Showing 18 changed files with 416 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ public interface DijkstraModule<N, E extends Edge<N>> {
* Whether or not the given edge should be considered for relaxation. The algorithm will ignore the edge and not
* follow it if this method returns {@code false}.
*
* @param edge The edge in question
* @param pathDestination The destination of the shortest path computation or {@code null} if not present
* @param edge The edge in question
* @param pathDestination The destination of the shortest path computation or {@code null} if not present
* @param tentativeDistance The current tentative distance when relaxing this edge
*
* @return {@code True} if the edge should be considered, {@code false} otherwise
*/
@SuppressWarnings({ "SameReturnValue", "MethodReturnAlwaysConstant", "BooleanMethodNameMustStartWithQuestion" })
default boolean doConsiderEdgeForRelaxation(@SuppressWarnings("unused") final E edge,
@SuppressWarnings("unused") final N pathDestination) {
@SuppressWarnings("unused") final N pathDestination,
final TentativeDistance<? extends N, E> tentativeDistance) {
return true;
}

Expand Down Expand Up @@ -61,6 +63,18 @@ default OptionalDouble provideEdgeCost(@SuppressWarnings("unused") final E edge,
return OptionalDouble.empty();
}

/**
* Whether or not the algorithm should abort computation of the shortest path. The method is called right before the
* given node will be settled.
*
* @param tentativeDistance The tentative distance wrapper of the node that will be settled next
*
* @return {@code True} if the computation should be aborted, {@code false} if not
*/
default boolean shouldAbortBefore(@SuppressWarnings("unused") final TentativeDistance<N, E> tentativeDistance) {
return false;
}

/**
* Whether or not the algorithm should abort computation of the shortest path. The method is called right after the
* given node has been settled.
Expand All @@ -69,7 +83,7 @@ default OptionalDouble provideEdgeCost(@SuppressWarnings("unused") final E edge,
*
* @return {@code True} if the computation should be aborted, {@code false} if not
*/
default boolean shouldAbort(@SuppressWarnings("unused") final TentativeDistance<N, E> tentativeDistance) {
default boolean shouldAbortAfter(@SuppressWarnings("unused") final TentativeDistance<N, E> tentativeDistance) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*
* @author Daniel Tischner {@literal <[email protected]>}
*/
public interface Path<N, E extends Edge<N>> extends Iterable<EdgeCost<N, E>> {
public interface Path<N, E extends Edge<N>> extends ReverselyIterable<EdgeCost<N, E>> {
/**
* Gets the destination node of the path.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package de.zabuza.maglev.external.algorithms;

import de.zabuza.maglev.external.graph.Edge;

import java.util.Collection;
import java.util.Optional;
import java.util.stream.Stream;

/**
* Interface for a shortest path tree. That is the result of a shortest path exploration from given sources to possibly
* multiple destinations.
* <p>
* It provides methods to construct the actual shortest path to a given destination and also to retrieve the leaves,
* i.e. the boundary of the exploration.
*
* @param <N> The type of nodes
* @param <E> The type of edges
*
* @author Daniel Tischner {@literal <[email protected]>}
*/
public interface PathTree<N, E extends Edge<N>> {
/**
* The sources of the shortest path exploration, i.e. the roots of the tree. This can be a single but also multiple
* nodes, depending on the type of exploration.
*
* @return The sources of the shortest path exploration, can also only contain a single node. The collection is not
* modifiable.
*/
Collection<N> getSources();

/**
* Gets a stream over all nodes that are reachable from the given sources, i.e. all nodes in the tree. This includes
* the sources themselves.
* <p>
* Implementations must construct the stream in {@code O(1)} time.
*
* @return All reachable nodes, including the sources
*/
Stream<N> getReachableNodes();

/**
* Constructs the shortest path from the closest of the given sources to the given destination, i.e. the path from
* the roots down to the given destination.
*
* @param destination The destination to construct the path to
*
* @return The shortest path from one of the sources to the given destination. Or empty if the destination is not
* reachable.
*/
Optional<Path<N, E>> getPathTo(N destination);

/**
* Gets the boundary nodes of the shortest path exploration, i.e. the leaves of the tree. Such nodes are farthest
* away from the given set of sources in their respective direction.
*
* @return The boundary nodes of the exploration, can include source nodes
*/
Collection<N> getLeaves();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package de.zabuza.maglev.external.algorithms;

import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;

/**
* Extension of {@link Iterable} for classes offering {@link java.util.Iterator}s that iterate the given data source
* reversely, in comparison to the order defined by the iterator returned by the {@link Iterable} implementation.
*
* @param <T> The type of the elements contained in the data source
*
* @author Daniel Tischner {@literal <[email protected]>}
*/
public interface ReverselyIterable<T> extends Iterable<T> {
/**
* Returns an iterator over elements of type {@code T} that iterates the data source reversely.
*
* @return A reverse iterator
*/
Iterator<T> reverseIterator();

/**
* Performs the given action for each element of the {@code ReverselyIterable} until all elements have been
* processed or the action throws an exception. Actions are performed in the order of iteration, if that order is
* specified. Exceptions thrown by the action are relayed to the caller.
* <p>
* The behavior of this method is unspecified if the action performs side-effects that modify the underlying source
* of elements, unless an overriding class has specified a concurrent modification policy.
*
* @param action The action to be performed for each element
*
* @throws NullPointerException if the specified action is null
*/
default void forEachReversed(final Consumer<? super T> action) {
Objects.requireNonNull(action);
final Iterator<T> reverseIter = reverseIterator();
while (reverseIter.hasNext()) {
action.accept(reverseIter.next());
}
}

/**
* Creates a {@link Spliterator} over the elements described by this {@code ReverselyIterable}.
*
* @return a {@code Spliterator} over the elements described by this {@code ReverselyIterable}.
*/
default Spliterator<T> spliteratorReversed() {
return Spliterators.spliteratorUnknownSize(reverseIterator(), 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,24 @@ public interface ShortestPathComputation<N, E extends Edge<N>> {
* @return A map which connects destination nodes to the costs of their shortest path
*/
Map<N, ? extends HasPathCost> shortestPathCostsReachable(N source);

/**
* Computes all shortest paths from the given sources to all other nodes.<br>
* <br>
* The shortest path from multiple sources is the minimal shortest path for all source nodes individually.
*
* @param sources The sources to compute the shortest path from
*
* @return A tree containing all computed shortest paths
*/
PathTree<N, E> shortestPathReachable(Collection<N> sources);

/**
* Computes the shortest paths from the given source to all other nodes.
*
* @param source The source to compute the shortest path from
*
* @return A tree containing all computed shortest paths
*/
PathTree<N, E> shortestPathReachable(N source);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
* The builder offers highly customizable algorithms based on Dijkstra, called Module-Dijkstra. That is a regular
* Dijkstra algorithm which can be extended using extension modules that modify its behavior. Offered modules are:
* <ul>
* <li>{@code AbortIfModule} - Aborts further computation as soon as a node that matches a given predicate has been settled</li>
* <li>{@code AbortAfterIfModule} - Aborts further computation as soon as a node that matches a given predicate has been settled</li>
* <li>{@code AbortBeforeIfModule} - Aborts further computation as soon as a node that matches a given predicate would be settled</li>
* <li>{@code AbortAfterRangeModule} - Only explores shortest paths up to the given range</li>
* <li>{@code IgnoreEdgeIfModule} - Ignores exploring edges that match the given predicate</li>
* <li>{@code AStarModule} - Optimization of the algorithm by utilizing a given heuristic metric</li>
Expand Down Expand Up @@ -179,6 +180,20 @@ public ShortestPathComputationBuilder<N, E> addModule(final DijkstraModule<N, E>
return this;
}

/**
* Adds a module to be used by {@code Module-Dijkstra} which aborts computation right before a node has been settled
* that matches the given predicate.
*
* @param predicate The predicate to test the node against, not null
*
* @return This builder instance
*/
public ShortestPathComputationBuilder<N, E> addModuleAbortBeforeIf(
final Predicate<? super TentativeDistance<N, E>> predicate) {
modules.add(AbortBeforeIfModule.of(Objects.requireNonNull(predicate)));
return this;
}

/**
* Adds a module to be used by {@code Module-Dijkstra} which aborts computation as soon as a node has been settled
* that matches the given predicate.
Expand All @@ -187,9 +202,9 @@ public ShortestPathComputationBuilder<N, E> addModule(final DijkstraModule<N, E>
*
* @return This builder instance
*/
public ShortestPathComputationBuilder<N, E> addModuleAbortIf(
public ShortestPathComputationBuilder<N, E> addModuleAbortAfterIf(
final Predicate<? super TentativeDistance<N, E>> predicate) {
modules.add(AbortIfModule.of(Objects.requireNonNull(predicate)));
modules.add(AbortAfterIfModule.of(Objects.requireNonNull(predicate)));
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
*
* @author Daniel Tischner {@literal <[email protected]>}
*/
public final class SimpleEdge<N> implements Edge<N>, ReversedConsumer {
@SuppressWarnings("DesignForExtension")
public class SimpleEdge<N> implements Edge<N>, ReversedConsumer {
/**
* The cost of the edge, i.e. its weight.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
*
* @author Daniel Tischner {@literal <[email protected]>}
*/
public final class SimpleGraph<N, E extends Edge<N> & ReversedConsumer> extends AbstractGraph<N, E>
@SuppressWarnings("DesignForExtension")
public class SimpleGraph<N, E extends Edge<N> & ReversedConsumer> extends AbstractGraph<N, E>
implements ReversedProvider {
/**
* A set with all contained nodes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
* @author Daniel Tischner {@literal <[email protected]>}
*/
public class AbortIfModule<N, E extends Edge<N>> implements DijkstraModule<N, E> {
public final class AbortAfterIfModule<N, E extends Edge<N>> implements DijkstraModule<N, E> {

/**
* Creates an module which aborts computation after exploring to a node which matches the given predicate.
Expand All @@ -28,9 +28,9 @@ public class AbortIfModule<N, E extends Edge<N>> implements DijkstraModule<N, E>
*
* @return The created module
*/
public static <N, E extends Edge<N>> AbortIfModule<N, E> of(
public static <N, E extends Edge<N>> AbortAfterIfModule<N, E> of(
final Predicate<? super TentativeDistance<N, E>> predicate) {
return new AbortIfModule<>(predicate);
return new AbortAfterIfModule<>(predicate);
}

/**
Expand All @@ -43,12 +43,12 @@ public static <N, E extends Edge<N>> AbortIfModule<N, E> of(
*
* @param predicate The predicate to test the node against
*/
AbortIfModule(final Predicate<? super TentativeDistance<N, E>> predicate) {
private AbortAfterIfModule(final Predicate<? super TentativeDistance<N, E>> predicate) {
this.predicate = predicate;
}

@Override
public final boolean shouldAbort(final TentativeDistance<N, E> tentativeDistance) {
public boolean shouldAbortAfter(final TentativeDistance<N, E> tentativeDistance) {
return predicate.test(tentativeDistance);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*
* @author Daniel Tischner {@literal <[email protected]>}
*/
public final class AbortAfterRangeModule<N, E extends Edge<N>> extends AbortIfModule<N, E> {
public final class AbortAfterRangeModule<N, E extends Edge<N>> extends AbortBeforeIfModule<N, E> {

/**
* Creates an module which aborts computation after exploring to the given range.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package de.zabuza.maglev.internal.algorithms.shortestpath;

import de.zabuza.maglev.external.algorithms.DijkstraModule;
import de.zabuza.maglev.external.algorithms.TentativeDistance;
import de.zabuza.maglev.external.graph.Edge;

import java.util.function.Predicate;

/**
* Module for a {@link ModuleDijkstra} that aborts computation of the shortest path before exploring a node which
* matches the given predicate.<br>
* <br>
* The factory method {@link #of(Predicate)} can be used for convenient instance creation.
*
* @param <N> Type of the nodes
* @param <E> Type of the edges
*
* @author Daniel Tischner {@literal <[email protected]>}
*/
public class AbortBeforeIfModule<N, E extends Edge<N>> implements DijkstraModule<N, E> {

/**
* Creates an module which aborts computation before exploring a node which matches the given predicate.
*
* @param <N> Type of the nodes
* @param <E> Type of the edges
* @param predicate The predicate to test the node against.
*
* @return The created module
*/
public static <N, E extends Edge<N>> AbortBeforeIfModule<N, E> of(
final Predicate<? super TentativeDistance<N, E>> predicate) {
return new AbortBeforeIfModule<>(predicate);
}

/**
* The predicate to test the node against.
*/
private final Predicate<? super TentativeDistance<N, E>> predicate;

/**
* Creates an module which aborts computation before exploring a node which matches the given predicate.
*
* @param predicate The predicate to test the node against
*/
AbortBeforeIfModule(final Predicate<? super TentativeDistance<N, E>> predicate) {
this.predicate = predicate;
}

@Override
public final boolean shouldAbortBefore(final TentativeDistance<N, E> tentativeDistance) {
return predicate.test(tentativeDistance);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import de.zabuza.maglev.external.algorithms.HasPathCost;
import de.zabuza.maglev.external.algorithms.Path;
import de.zabuza.maglev.external.algorithms.PathTree;
import de.zabuza.maglev.external.algorithms.ShortestPathComputation;
import de.zabuza.maglev.external.graph.Edge;

Expand Down Expand Up @@ -42,4 +43,9 @@ public Optional<Double> shortestPathCost(final N source, final N destination) {
return shortestPathCostsReachable(Collections.singletonList(source));
}

@Override
public PathTree<N, E> shortestPathReachable(final N source) {
return shortestPathReachable(Collections.singletonList(source));
}

}
Loading

0 comments on commit c8315f6

Please sign in to comment.