package org.aksw.jena_sparql_api_sparql_path2; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import org.aksw.jena_sparql_api.lookup.LookupService; import org.aksw.jena_sparql_api.sparql_path2.Nfa; import org.aksw.jena_sparql_api.sparql_path2.NfaExecutionUtils; import org.aksw.jena_sparql_api.sparql_path2.ValueSet; import org.aksw.jena_sparql_api.utils.Pair; import org.aksw.jena_sparql_api.utils.model.Directed; import org.aksw.jena_sparql_api.utils.model.Triplet; import org.aksw.jena_sparql_api.utils.model.TripletPath; import org.apache.jena.ext.com.google.common.collect.Lists; import org.apache.jena.graph.Node; public class YensKShortestPaths { // // public static List<TripletPath<Node, Node>> findPaths( // Nfa<Integer, LabeledEdge<Integer, PredicateClass>> nfa, // Object isEpsilon, // Function<T, ? extends Pair<ValueSet<V>>> transToVertexClass, // Function<Pair<ValueSet<Node>>, LookupService<Node, Set<Triplet<Node, Node>>>> createTripletLookupService, // Node startNode, Node endNode, int maxK) { // // TODO Auto-generated method stub // return null; // } // // // public static List<TripletPath<Node, Node>> findPaths( // Nfa<Integer, LabeledEdge<Integer, PredicateClass>> nfa, // Predicate<T> isEpsilon, // Function<T, ? extends Pair<ValueSet<V>>> transToVertexClass, // Function<Pair<ValueSet<Node>>, LookupService<Node, Set<Triplet<Node, Node>>>> createTripletLookupService, // Node startNode, Node endNode, int maxK) { // // TODO Auto-generated method stub // return null; // } public static <S, T, V, E> List<TripletPath<Entry<S, V>, Directed<E>>> findPaths( Nfa<S, T> nfa, Predicate<T> isEpsilon, Function<T, ? extends Pair<ValueSet<V>>> transToVertexClass, Function<Pair<ValueSet<V>>, ? extends Function<? super Iterable<V>, Map<V, Set<Triplet<V,E>>>>> createTripletLookupService, V source, V target, int maxK) { //BiFunction<Iterable<Entry<S, V>>, Integer, Map<Entry<S, V>, Set<Triplet<Entry<S, V>, Directed<E>>>>> successors = //NfaSuccessor<S, T, V, E> successors = Function<Iterable<Entry<S, V>>, Map<Entry<S, V>, Set<Triplet<Entry<S, V>, Directed<E>>>>> successors = new NfaSuccessor<S,T,V,E>(nfa, isEpsilon, transToVertexClass, createTripletLookupService); Set<Entry<S, V>> starts = new HashSet<>(); nfa.getStartStates().forEach(s -> starts.add(new SimpleEntry<>(s, source))); List<TripletPath<Entry<S, V>, Directed<E>>> result = findPaths( successors, starts, e -> { S state = e.getKey(); V vertex = e.getValue(); boolean isAcceptingState = NfaExecutionUtils.isFinalState(nfa, state, isEpsilon);//nfa.getEndStates().contains(state); boolean isTargetVertex = target.equals(vertex); boolean r = isAcceptingState && isTargetVertex; return r; }, maxK); return result; } // Adapted from https://en.wikipedia.org/wiki/Yen's_algorithm //Graph<V, E> graph, /** * The successor function maps each vertex to a set of triplets. * The triplets must have the vertex either as the subject or the object * * * * @param successors * @param source * @param target * @param maxK * @return */ public static <V, E> List<TripletPath<V, Directed<E>>> findPaths( Function<? super Iterable<V>, Map<V, Set<Triplet<V, Directed<E>>>>> successors, //Function<Iterable<Entry<S, V>>, Map<Entry<S, V>, Set<Triplet<Entry<S, V>, Directed<E>>>>> //BiFunction<Collection<Entry<S, V>>, Integer, Multimap<Entry<S, V>, Triplet<Entry<S, V>, Directed<E>>>> Collection<V> sources, Predicate<V> isTarget, int maxK) { List<TripletPath<V, Directed<E>>> A = new ArrayList<>(); List<TripletPath<V, Directed<E>>> B = new ArrayList<>(); Set<Triplet<V, Directed<E>>> removedTriplets = new HashSet<>(); Set<V> removedNodes = new HashSet<V>(); /** * Adapted successor function * */ Function<Iterable<V>, Map<V, Set<Triplet<V, Directed<E>>>>> succ = rawNodes -> { Set<V> nodes = Lists.newArrayList(rawNodes).stream() .filter(x -> !removedNodes.contains(x)) .collect(Collectors.toSet()); Map<V, Set<Triplet<V, Directed<E>>>> tmp = successors.apply(nodes); tmp.entrySet().removeIf(removedNodes::contains); tmp.entrySet().forEach(e -> { Set<Triplet<V, Directed<E>>> ts = e.getValue(); ts.removeIf(triplet -> { boolean skip = removedTriplets.contains(triplet) || removedNodes.contains(triplet.getSubject()) || removedNodes.contains(triplet.getObject()); return skip; }); }); return tmp; // TODO remove all removed edges from the successors }; // Determine the shortest path from the source to the sink. TripletPath<V, Directed<E>> path = NfaDijkstra.dijkstra(successors, sources, isTarget); if(path != null) { A.add(path); for(int k = 1; k < maxK; ++k) { TripletPath<V, Directed<E>> ak_1 = A.get(k - 1); int akl = ak_1.getLength(); // TODO getLength returns only counts the triplets but not the vertices for(int i = 0; i < akl; ++i) { if(k == 3 && i == 2) { System.out.println("DEBUG condition"); } V spurNode = ak_1.getNode(i); TripletPath<V, Directed<E>> rootPath = ak_1.subPath(0, i); for(TripletPath<V, Directed<E>> a : A) { if(a.getLength() >= rootPath.getLength()) { TripletPath<V, Directed<E>> subPath = a.subPath(0, i); if(rootPath.equals(subPath)) { //p.nodes(0, i): // Remove the links that are part of the previous shortest paths which share the same root path. Triplet<V, Directed<E>> triplet = a.getTriplets().get(i); //get the triplet (i, i + 1) //removedTriplets.add(Triplet.makeUndirected(triplet)); removedTriplets.add(triplet); } } } //for each node rootPathNode in rootPath except spurNode: // remove rootPathNode from Graph; Set<V> rootPathNodes = rootPath.getNodeSet(); rootPathNodes.remove(spurNode); removedNodes.addAll(rootPathNodes); // Calculate the spur path from the spur node to the sink. TripletPath<V, Directed<E>> spurPath = NfaDijkstra.dijkstra(succ, Collections.singleton(spurNode), isTarget); if(spurPath != null) { // Entire path is made up of the root path and spur path. TripletPath<V, Directed<E>> totalPath = rootPath.concat(spurPath); System.out.println("GOT TOTAL PATH: " + totalPath); // Add the potential k-shortest path to the heap. B.add(totalPath); } // Add back the edges and nodes that were removed from the graph. removedNodes.clear(); removedTriplets.clear(); } if(B.isEmpty()) { // This handles the case of there being no spur paths, or no spur paths left. // This could happen if the spur paths have already been exhausted (added to A), // or there are no spur paths at all - such as when both the source and sink vertices // lie along a "dead end". break; } // Sort the potential k-shortest paths by cost. //B.sort(); // (all costs are equal in our use case) // Add the lowest cost path becomes the k-shortest path. int l = B.size() - 1; TripletPath<V, Directed<E>> chosenPath = B.remove(l); //B.get(l); A.add(chosenPath); } } return A; } }