All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.onlab.graph.KShortestPathsSearch Maven / Gradle / Ivy

There is a newer version: 2.7.0
Show newest version
/*
 * Copyright 2015-present Open Networking Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onlab.graph;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.slf4j.Logger;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;

/**
 * Runs K shortest paths algorithm on a provided directed graph.  Returns results in the form of an
 * InnerOrderedResult so iteration through the returned paths will return paths in ascending order according to the
 * provided EdgeWeight.
 */
public class KShortestPathsSearch> extends AbstractGraphPathSearch {

    private final Logger log = getLogger(getClass());

    @Override
    protected Result internalSearch(Graph graph, V src, V dst, EdgeWeigher weigher, int maxPaths) {
        //The modified edge weigher removes any need to modify the original graph
        InnerEdgeWeigher modifiedWeighter = new InnerEdgeWeigher(checkNotNull(weigher));
        checkArgument(maxPaths != ALL_PATHS, "KShortestPath search cannot" +
                "be used with ALL_PATHS.");
        checkArgument(maxPaths > 0, "The max number of paths must be greater" +
                " than 0");
        Graph originalGraph = checkNotNull(graph);
        //the result contains the set of eventual results
        InnerOrderedResult result = new InnerOrderedResult(src, dst, maxPaths);
        ArrayList> resultPaths = new ArrayList<>(maxPaths);
        ArrayList> potentialPaths = Lists.newArrayList();

        DijkstraGraphSearch dijkstraSearch = new DijkstraGraphSearch<>();
        Set> dijkstraResults = dijkstraSearch.search(originalGraph, src, dst, modifiedWeighter, 1).paths();
        //Checks if the dst was reachable
        if (dijkstraResults.isEmpty()) {
            log.warn("No path was found.");
            return result;
        }
        //If it was reachable adds the first shortest path to the set of results
        resultPaths.add(dijkstraResults.iterator().next());

        for (int k = 1; k < maxPaths; k++) {

            for (int i = 0; i < resultPaths.get(k - 1).edges().size(); i++) {
                V spurNode = resultPaths.get(k - 1).edges().get(i).src();
                List rootPathEdgeList = resultPaths.get(k - 1).edges().subList(0, i);

                for (Path path : resultPaths) {
                    if (path.edges().size() >= i && edgeListsAreEqual(rootPathEdgeList, path.edges().subList(0, i))) {
                        modifiedWeighter.removedEdges.add(path.edges().get(i));
                    }
                }

                //Effectively remove all nodes from the source path
                for (E edge : rootPathEdgeList) {
                    originalGraph.getEdgesFrom(edge.src()).forEach(e -> modifiedWeighter.removedEdges.add(e));
                    originalGraph.getEdgesTo(edge.src()).forEach(e -> modifiedWeighter.removedEdges.add(e));
                }

                dijkstraResults = dijkstraSearch.search(originalGraph, spurNode, dst, modifiedWeighter, 1).paths();
                if (!dijkstraResults.isEmpty()) {
                    Path spurPath = dijkstraResults.iterator().next();
                    List totalPath = new ArrayList<>(rootPathEdgeList);
                    spurPath.edges().forEach(totalPath::add);
                    //The following line must use the original weigher not the modified weigher because the modified
                    //weigher will count -1 values used for modifying the graph and return an inaccurate cost.
                    potentialPaths.add(new DefaultPath<>(totalPath,
                            calculatePathCost(weigher, totalPath)));
                }

                //Restore all removed paths and nodes
                modifiedWeighter.removedEdges.clear();
            }
            if (potentialPaths.isEmpty()) {
                break;
            }
            potentialPaths.sort(new InnerPathComparator());
            resultPaths.add(potentialPaths.get(0));
            potentialPaths.remove(0);
        }
        result.pathSet.addAll(resultPaths);

        return result;
    }
    //Edge list equality is judges by shared endpoints, and shared endpoints should be the same
    private boolean edgeListsAreEqual(List edgeListOne, List edgeListTwo) {
        if (edgeListOne.size() != edgeListTwo.size()) {
            return false;
        }
        E edgeOne;
        E edgeTwo;
        for (int i = 0; i < edgeListOne.size(); i++) {
            edgeOne = edgeListOne.get(i);
            edgeTwo = edgeListTwo.get(i);
            if (!edgeOne.equals(edgeTwo)) {
                return false;
            }
        }
        return true;
    }

    private Weight calculatePathCost(EdgeWeigher weighter, List edges) {
        Weight totalCost = weighter.getInitialWeight();
        for (E edge : edges) {
            totalCost = totalCost.merge(weighter.weight(edge));
        }
        return totalCost;
    }

    /**
     * Weights edges to make them inaccessible if set, otherwise returns the result of the original EdgeWeight.
     */
    private final class InnerEdgeWeigher implements EdgeWeigher {

        private Set removedEdges = Sets.newConcurrentHashSet();
        private EdgeWeigher innerEdgeWeigher;

        private InnerEdgeWeigher(EdgeWeigher weigher) {
            this.innerEdgeWeigher = weigher;
        }

        @Override
        public Weight weight(E edge) {
            if (removedEdges.contains(edge)) {
                return innerEdgeWeigher.getNonViableWeight();
            }
            return innerEdgeWeigher.weight(edge);
        }

        @Override
        public Weight getInitialWeight() {
            return innerEdgeWeigher.getInitialWeight();
        }

        @Override
        public Weight getNonViableWeight() {
            return innerEdgeWeigher.getNonViableWeight();
        }
    }

    /**
     * A result modified to return paths ordered according to the provided comparator.
     */
    protected class InnerOrderedResult extends DefaultResult {

        private TreeSet> pathSet = new TreeSet<>(new InnerPathComparator());

        public InnerOrderedResult(V src, V dst) {
            super(src, dst);
        }

        public InnerOrderedResult(V src, V dst, int maxPaths) {
            super(src, dst, maxPaths);
        }

        @Override
        public Set> paths() {
            return ImmutableSet.copyOf(pathSet);
        }
    }

    /**
     * Provides a comparator to order the set of paths.
     */
    private class InnerPathComparator implements Comparator> {

        @Override
        public int compare(Path pathOne, Path pathTwo) {
            int comparisonValue = pathOne.cost().compareTo(pathTwo.cost());
            if  (comparisonValue != 0) {
                return comparisonValue;
            } else if (edgeListsAreEqual(pathOne.edges(), pathTwo.edges())) {
                return 0;
            } else {
                return 1;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy